From 294294646ae986046f11a752f3257b05ea4bf6fb Mon Sep 17 00:00:00 2001 From: David Farler Date: Tue, 17 Mar 2015 12:00:39 -0700 Subject: [PATCH 001/742] Add APINotes cases for TvOS and WatchOS rdar://problem/20192169 --- include/clang/APINotes/APINotesYAMLCompiler.h | 2 ++ tools/driver/apinotes_main.cpp | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/include/clang/APINotes/APINotesYAMLCompiler.h b/include/clang/APINotes/APINotesYAMLCompiler.h index e897b5cf904..f459e079d25 100644 --- a/include/clang/APINotes/APINotesYAMLCompiler.h +++ b/include/clang/APINotes/APINotesYAMLCompiler.h @@ -35,6 +35,8 @@ namespace api_notes { enum class OSType { OSX, IOS, + TvOS, + WatchOS, Absent }; diff --git a/tools/driver/apinotes_main.cpp b/tools/driver/apinotes_main.cpp index 76892b69ba4..cbd8045922d 100644 --- a/tools/driver/apinotes_main.cpp +++ b/tools/driver/apinotes_main.cpp @@ -104,6 +104,12 @@ int cc1apinotes_main(ArrayRef Argv, const char *Argv0, case llvm::Triple::IOS: targetOS = api_notes::OSType::IOS; break; + case llvm::Triple::WatchOS: + targetOS = api_notes::OSType::WatchOS; + break; + case llvm::Triple::TvOS: + targetOS = api_notes::OSType::TvOS; + break; default: errs() << "target is not supported\n"; return 1; From 7be46ab8a200f604df12e314d4bbf07cdfe81f84 Mon Sep 17 00:00:00 2001 From: Greg Clayton Date: Tue, 26 May 2015 10:02:30 -0700 Subject: [PATCH 002/742] LLDB needs access to RecordDecl::LoadedFieldsFromExternalStorage member variable. Adding accessors to this variable. Needed for module debug info: --- include/clang/AST/Decl.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index cc778a550a7..434b45651ba 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -3284,6 +3284,9 @@ class RecordDecl : public TagDecl { bool hasFlexibleArrayMember() const { return HasFlexibleArrayMember; } void setHasFlexibleArrayMember(bool V) { HasFlexibleArrayMember = V; } + bool hasLoadedFieldsFromExternalStorage() const { return LoadedFieldsFromExternalStorage; } + void setHasLoadedFieldsFromExternalStorage(bool V) { LoadedFieldsFromExternalStorage = V; } + /// isAnonymousStructOrUnion - Whether this is an anonymous struct /// or union. To be an anonymous struct or union, it must have been /// declared without a name and there must be no objects of this From 33f6a3de6f1b3a2bf400389a6bcb0de34c8c4b6a Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Tue, 10 Nov 2015 13:31:33 -0800 Subject: [PATCH 003/742] Allow modules specified by -fmodule-map-file to shadow implicitly found ones When modules come from module map files explicitly specified by -fmodule-map-file= arguments, allow those to override/shadow modules with the same name that are found implicitly by header search. If such a module is looked up by name (e.g. @import), we will always find the one from -fmodule-map-file. If we try to use a shadowed module by including one of its headers report an error. This enables developers to force use of a specific copy of their module to be used if there are multiple copies that would otherwise be visible, for example if they develop modules that are installed in the default search paths. rdar://problem/23467372 --- include/clang/Basic/DiagnosticCommonKinds.td | 2 + include/clang/Basic/Module.h | 18 ++++-- include/clang/Lex/ModuleMap.h | 29 +++++++++- lib/Basic/Module.cpp | 7 ++- lib/Frontend/CompilerInstance.cpp | 7 ++- lib/Frontend/FrontendAction.cpp | 7 +++ lib/Frontend/FrontendActions.cpp | 7 ++- lib/Lex/ModuleMap.cpp | 58 ++++++++++++++----- lib/Lex/PPDirectives.cpp | 6 +- test/Modules/Inputs/shadow/A1/A.h | 1 + .../Modules/Inputs/shadow/A1/module.modulemap | 5 ++ test/Modules/Inputs/shadow/A2/A.h | 1 + .../Modules/Inputs/shadow/A2/module.modulemap | 5 ++ test/Modules/shadow.m | 21 +++++++ 14 files changed, 151 insertions(+), 23 deletions(-) create mode 100644 test/Modules/Inputs/shadow/A1/A.h create mode 100644 test/Modules/Inputs/shadow/A1/module.modulemap create mode 100644 test/Modules/Inputs/shadow/A2/A.h create mode 100644 test/Modules/Inputs/shadow/A2/module.modulemap create mode 100644 test/Modules/shadow.m diff --git a/include/clang/Basic/DiagnosticCommonKinds.td b/include/clang/Basic/DiagnosticCommonKinds.td index dc28200da94..e7d9354ae1d 100644 --- a/include/clang/Basic/DiagnosticCommonKinds.td +++ b/include/clang/Basic/DiagnosticCommonKinds.td @@ -88,6 +88,8 @@ def err_module_unavailable : Error< "module '%0' %select{is incompatible with|requires}1 feature '%2'">; def err_module_header_missing : Error< "%select{|umbrella }0header '%1' not found">; +def err_module_shadowed : Error< + "import of shadowed module '%0'">; def err_module_lock_failure : Error< "could not acquire lock file for module '%0'">, DefaultFatal; def err_module_lock_timeout : Error< diff --git a/include/clang/Basic/Module.h b/include/clang/Basic/Module.h index fa032e9eb66..087320bd2c3 100644 --- a/include/clang/Basic/Module.h +++ b/include/clang/Basic/Module.h @@ -149,6 +149,9 @@ class Module { /// will be false to indicate that this (sub)module is not available. SmallVector Requirements; + /// \brief A module with the same name that shadows this module. + Module *ShadowingModule = nullptr; + /// \brief Whether this module is missing a feature from \c Requirements. unsigned IsMissingRequirement : 1; @@ -323,13 +326,20 @@ class Module { /// /// \param Target The target options used for the current translation unit. /// - /// \param Req If this module is unavailable, this parameter - /// will be set to one of the requirements that is not met for use of - /// this module. + /// \param Req If this module is unavailable because of a missing requirement, + /// this parameter will be set to one of the requirements that is not met for + /// use of this module. + /// + /// \param MissingHeader If this module is unavailable because of a missing + /// header, this parameter will be set to one of the missing headers. + /// + /// \param ShadowingModule If this module is unavailable because it is + /// shadowed, this parameter will be set to the shadowing module. bool isAvailable(const LangOptions &LangOpts, const TargetInfo &Target, Requirement &Req, - UnresolvedHeaderDirective &MissingHeader) const; + UnresolvedHeaderDirective &MissingHeader, + Module *&ShadowingModule) const; /// \brief Determine whether this module is a submodule. bool isSubModule() const { return Parent != nullptr; } diff --git a/include/clang/Lex/ModuleMap.h b/include/clang/Lex/ModuleMap.h index 155943e5453..6d171b24f24 100644 --- a/include/clang/Lex/ModuleMap.h +++ b/include/clang/Lex/ModuleMap.h @@ -79,7 +79,7 @@ class ModuleMap { std::string SourceModuleName; private: - /// \brief The top-level modules that are known. + /// \brief The unshadowed top-level modules that are known. llvm::StringMap Modules; /// \brief The number of modules we have created in total. @@ -155,6 +155,15 @@ class ModuleMap { /// header. llvm::DenseMap UmbrellaDirs; + /// \brief A generation counter that is used to test whether modules of the + /// same name may shadow or are illegal redefintions. + /// + /// Modules from earlier scopes may shadow modules from later ones. + /// Modules from the same scope may not have the same name. + unsigned CurrentModuleScopeID = 0; + + llvm::DenseMap ModuleScopeIDs; + /// \brief The set of attributes that can be attached to a module. struct Attributes { Attributes() : IsSystem(), IsExternC(), IsExhaustive() {} @@ -388,6 +397,24 @@ class ModuleMap { Module *inferFrameworkModule(const DirectoryEntry *FrameworkDir, bool IsSystem, Module *Parent); + /// \brief Create a new top-level module that is shadowed by + /// \p ShadowingModule. + Module *createShadowedModule(StringRef Name, bool IsFramework, + Module *ShadowingModule); + + /// \brief Creates a new declaration scope for module names, allowing + /// previously defined modules to shadow definitions from the new scope. + /// + /// \note Module names from earlier scopes will shadow names from the new + /// scope, which is the opposite of how shadowing works for variables. + void finishModuleDeclarationScope() { CurrentModuleScopeID += 1; } + + bool mayShadowNewModule(Module *ExistingModule) { + assert(!ExistingModule->Parent && "expected top-level module"); + assert(ModuleScopeIDs.count(ExistingModule) && "unknown module"); + return ModuleScopeIDs[ExistingModule] < CurrentModuleScopeID; + } + /// \brief Retrieve the module map file containing the definition of the given /// module. /// diff --git a/lib/Basic/Module.cpp b/lib/Basic/Module.cpp index 0b783263694..a8656749907 100644 --- a/lib/Basic/Module.cpp +++ b/lib/Basic/Module.cpp @@ -79,11 +79,16 @@ static bool hasFeature(StringRef Feature, const LangOptions &LangOpts, bool Module::isAvailable(const LangOptions &LangOpts, const TargetInfo &Target, Requirement &Req, - UnresolvedHeaderDirective &MissingHeader) const { + UnresolvedHeaderDirective &MissingHeader, + Module *&ShadowingModule) const { if (IsAvailable) return true; for (const Module *Current = this; Current; Current = Current->Parent) { + if (Current->ShadowingModule) { + ShadowingModule = Current->ShadowingModule; + return false; + } for (unsigned I = 0, N = Current->Requirements.size(); I != N; ++I) { if (hasFeature(Current->Requirements[I].first, LangOpts, Target) != Current->Requirements[I].second) { diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp index c0cf64cecbe..57ec9c47a05 100644 --- a/lib/Frontend/CompilerInstance.cpp +++ b/lib/Frontend/CompilerInstance.cpp @@ -1605,8 +1605,13 @@ CompilerInstance::loadModule(SourceLocation ImportLoc, // Check whether this module is available. clang::Module::Requirement Requirement; clang::Module::UnresolvedHeaderDirective MissingHeader; + clang::Module *ShadowingModule = nullptr; if (!Module->isAvailable(getLangOpts(), getTarget(), Requirement, - MissingHeader)) { + MissingHeader, ShadowingModule)) { + + assert(!ShadowingModule && + "lookup of module by name should never find shadowed module"); + if (MissingHeader.FileNameLoc.isValid()) { getDiagnostics().Report(MissingHeader.FileNameLoc, diag::err_module_header_missing) diff --git a/lib/Frontend/FrontendAction.cpp b/lib/Frontend/FrontendAction.cpp index ecef92e0a7d..005bbf6e274 100644 --- a/lib/Frontend/FrontendAction.cpp +++ b/lib/Frontend/FrontendAction.cpp @@ -394,6 +394,13 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, CI.getDiagnostics().Report(diag::err_module_map_not_found) << Filename; } + // Add a module declaration scope so that modules from -fmodule-map-file + // arguments may shadow modules found implicitly in search paths. + CI.getPreprocessor() + .getHeaderSearchInfo() + .getModuleMap() + .finishModuleDeclarationScope(); + // If we were asked to load any module files, do so now. for (const auto &ModuleFile : CI.getFrontendOpts().ModuleFiles) if (!CI.loadModuleFile(ModuleFile)) diff --git a/lib/Frontend/FrontendActions.cpp b/lib/Frontend/FrontendActions.cpp index 865fb474020..8b0eff2d6ed 100644 --- a/lib/Frontend/FrontendActions.cpp +++ b/lib/Frontend/FrontendActions.cpp @@ -321,8 +321,13 @@ bool GenerateModuleAction::BeginSourceFileAction(CompilerInstance &CI, // Check whether we can build this module at all. clang::Module::Requirement Requirement; clang::Module::UnresolvedHeaderDirective MissingHeader; + clang::Module *ShadowingModule = nullptr; if (!Module->isAvailable(CI.getLangOpts(), CI.getTarget(), Requirement, - MissingHeader)) { + MissingHeader, ShadowingModule)) { + + assert(!ShadowingModule && + "lookup of module by name should never find shadowed module"); + if (MissingHeader.FileNameLoc.isValid()) { CI.getDiagnostics().Report(MissingHeader.FileNameLoc, diag::err_module_header_missing) diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp index c66bd70487f..d997aedabc0 100644 --- a/lib/Lex/ModuleMap.cpp +++ b/lib/Lex/ModuleMap.cpp @@ -562,6 +562,7 @@ ModuleMap::findOrCreateModule(StringRef Name, Module *Parent, bool IsFramework, } if (!Parent) { Modules[Name] = Result; + ModuleScopeIDs[Result] = CurrentModuleScopeID; if (!LangOpts.CurrentModule.empty() && !CompilingModule && Name == LangOpts.CurrentModule) { CompilingModule = Result; @@ -570,6 +571,20 @@ ModuleMap::findOrCreateModule(StringRef Name, Module *Parent, bool IsFramework, return std::make_pair(Result, true); } +Module *ModuleMap::createShadowedModule(StringRef Name, bool IsFramework, + Module *ShadowingModule) { + + // Create a new module with this name. + Module *Result = + new Module(Name, SourceLocation(), /*Parent=*/nullptr, IsFramework, + /*IsExplicit=*/false, NumCreatedModules++); + Result->ShadowingModule = ShadowingModule; + Result->IsAvailable = false; + ModuleScopeIDs[Result] = CurrentModuleScopeID; + + return Result; +} + /// \brief For a framework module, infer the framework against which we /// should link. static void inferFrameworkLink(Module *Mod, const DirectoryEntry *FrameworkDir, @@ -682,6 +697,8 @@ Module *ModuleMap::inferFrameworkModule(const DirectoryEntry *FrameworkDir, Module *Result = new Module(ModuleName, SourceLocation(), Parent, /*IsFramework=*/true, /*IsExplicit=*/false, NumCreatedModules++); + if (!Parent) + ModuleScopeIDs[Result] = CurrentModuleScopeID; InferredModuleAllowedBy[Result] = ModuleMapFile; Result->IsInferred = true; if (LangOpts.CurrentModule == ModuleName) { @@ -1412,6 +1429,7 @@ void ModuleMapParser::parseModuleDecl() { SourceLocation LBraceLoc = consumeToken(); // Determine whether this (sub)module has already been defined. + Module *ShadowingModule = nullptr; if (Module *Existing = Map.lookupModuleQualified(ModuleName, ActiveModule)) { if (Existing->DefinitionLoc.isInvalid() && !ActiveModule) { // Skip the module definition. @@ -1425,23 +1443,35 @@ void ModuleMapParser::parseModuleDecl() { } return; } - - Diags.Report(ModuleNameLoc, diag::err_mmap_module_redefinition) - << ModuleName; - Diags.Report(Existing->DefinitionLoc, diag::note_mmap_prev_definition); - - // Skip the module definition. - skipUntil(MMToken::RBrace); - if (Tok.is(MMToken::RBrace)) - consumeToken(); - - HadError = true; - return; + + if (!Existing->Parent && Map.mayShadowNewModule(Existing)) { + ShadowingModule = Existing; + } else { + // This is not a shawdowed module decl, it is an illegal redefinition. + Diags.Report(ModuleNameLoc, diag::err_mmap_module_redefinition) + << ModuleName; + Diags.Report(Existing->DefinitionLoc, diag::note_mmap_prev_definition); + + // Skip the module definition. + skipUntil(MMToken::RBrace); + if (Tok.is(MMToken::RBrace)) + consumeToken(); + + HadError = true; + return; + } } // Start defining this module. - ActiveModule = Map.findOrCreateModule(ModuleName, ActiveModule, Framework, - Explicit).first; + if (ShadowingModule) { + ActiveModule = + Map.createShadowedModule(ModuleName, Framework, ShadowingModule); + } else { + ActiveModule = + Map.findOrCreateModule(ModuleName, ActiveModule, Framework, Explicit) + .first; + } + ActiveModule->DefinitionLoc = ModuleNameLoc; if (Attrs.IsSystem || IsSystem) ActiveModule->IsSystem = true; diff --git a/lib/Lex/PPDirectives.cpp b/lib/Lex/PPDirectives.cpp index fd16168203a..01f680c3429 100644 --- a/lib/Lex/PPDirectives.cpp +++ b/lib/Lex/PPDirectives.cpp @@ -1683,13 +1683,17 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc, if (!SuggestedModule.getModule()->isAvailable()) { clang::Module::Requirement Requirement; clang::Module::UnresolvedHeaderDirective MissingHeader; + clang::Module *ShadowingModule = nullptr; Module *M = SuggestedModule.getModule(); // Identify the cause. (void)M->isAvailable(getLangOpts(), getTargetInfo(), Requirement, - MissingHeader); + MissingHeader, ShadowingModule); if (MissingHeader.FileNameLoc.isValid()) { Diag(MissingHeader.FileNameLoc, diag::err_module_header_missing) << MissingHeader.IsUmbrella << MissingHeader.FileName; + } else if (ShadowingModule) { + Diag(M->DefinitionLoc, diag::err_module_shadowed) << M->Name; + Diag(ShadowingModule->DefinitionLoc, diag::note_previous_definition); } else { Diag(M->DefinitionLoc, diag::err_module_unavailable) << M->getFullModuleName() << Requirement.second << Requirement.first; diff --git a/test/Modules/Inputs/shadow/A1/A.h b/test/Modules/Inputs/shadow/A1/A.h new file mode 100644 index 00000000000..f07c681c2aa --- /dev/null +++ b/test/Modules/Inputs/shadow/A1/A.h @@ -0,0 +1 @@ +#define A1_A_h diff --git a/test/Modules/Inputs/shadow/A1/module.modulemap b/test/Modules/Inputs/shadow/A1/module.modulemap new file mode 100644 index 00000000000..9439a431b1d --- /dev/null +++ b/test/Modules/Inputs/shadow/A1/module.modulemap @@ -0,0 +1,5 @@ +module A { + header "A.h" +} + +module A1 {} diff --git a/test/Modules/Inputs/shadow/A2/A.h b/test/Modules/Inputs/shadow/A2/A.h new file mode 100644 index 00000000000..9880ed010f5 --- /dev/null +++ b/test/Modules/Inputs/shadow/A2/A.h @@ -0,0 +1 @@ +#define A2_A_h diff --git a/test/Modules/Inputs/shadow/A2/module.modulemap b/test/Modules/Inputs/shadow/A2/module.modulemap new file mode 100644 index 00000000000..935d89bb425 --- /dev/null +++ b/test/Modules/Inputs/shadow/A2/module.modulemap @@ -0,0 +1,5 @@ +module A { + header "A.h" +} + +module A2 {} diff --git a/test/Modules/shadow.m b/test/Modules/shadow.m new file mode 100644 index 00000000000..44320af2b0c --- /dev/null +++ b/test/Modules/shadow.m @@ -0,0 +1,21 @@ +// RUN: rm -rf %t +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -I %S/Inputs/shadow/A1 -I %S/Inputs/shadow/A2 %s -fsyntax-only 2>&1 | FileCheck %s -check-prefix=REDEFINITION +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -fmodule-map-file=%S/Inputs/shadow/A1/module.modulemap -fmodule-map-file=%S/Inputs/shadow/A2/module.modulemap %s -fsyntax-only 2>&1 | FileCheck %s -check-prefix=REDEFINITION +// REDEFINITION: error: redefinition of module 'A' +// REDEFINITION: note: previously defined + +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -fmodule-map-file=%S/Inputs/shadow/A1/module.modulemap -I %S/Inputs/shadow %s -verify + +@import A1; +@import A2; +@import A; + +#import "A2/A.h" // expected-note {{implicitly imported}} +// expected-error@A2/module.modulemap:1 {{import of shadowed module 'A'}} +// expected-note@A1/module.modulemap:1 {{previous definition}} + +#if defined(A2_A_h) +#error got the wrong definition of module A +#elif !defined(A1_A_h) +#error missing definition from A1 +#endif From 6d317b334ce13fcc53c51dfa0f95d50fa60b2ba5 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Thu, 12 Nov 2015 00:46:57 +0000 Subject: [PATCH 004/742] [CMake] Setup an install component for libclang and c-index-test. Also don't create libclang dylib symlinks on darwin. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@252836 91177308-0d34-0410-b5e6-96231b3b80d8 --- CMakeLists.txt | 17 +++++++++++++++-- tools/c-index-test/CMakeLists.txt | 9 +++++++++ tools/libclang/CMakeLists.txt | 14 ++++++-------- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3aa0baeffae..2a78c6ca909 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -354,7 +354,7 @@ endmacro() macro(add_clang_library name) cmake_parse_arguments(ARG - "" + "SHARED" "" "ADDITIONAL_HEADERS" ${ARGN}) @@ -390,17 +390,29 @@ macro(add_clang_library name) ${ARG_ADDITIONAL_HEADERS} # It may contain unparsed unknown args. ) endif() - llvm_add_library(${name} ${ARG_UNPARSED_ARGUMENTS} ${srcs}) + if(ARG_SHARED) + set(ARG_ENABLE_SHARED SHARED) + endif() + llvm_add_library(${name} ${ARG_ENABLE_SHARED} ${ARG_UNPARSED_ARGUMENTS} ${srcs}) if(TARGET ${name}) target_link_libraries(${name} ${cmake_2_8_12_INTERFACE} ${LLVM_COMMON_LIBS}) if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY OR ${name} STREQUAL "libclang") install(TARGETS ${name} + COMPONENT ${name} EXPORT ClangTargets LIBRARY DESTINATION lib${LLVM_LIBDIR_SUFFIX} ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX} RUNTIME DESTINATION bin) + + if (${ARG_SHARED} AND NOT CMAKE_CONFIGURATION_TYPES) + add_custom_target(install-${name} + DEPENDS ${name} + COMMAND "${CMAKE_COMMAND}" + -DCMAKE_INSTALL_COMPONENT=${name} + -P "${CMAKE_BINARY_DIR}/cmake_install.cmake") + endif() endif() set_property(GLOBAL APPEND PROPERTY CLANG_EXPORTS ${name}) else() @@ -451,6 +463,7 @@ if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) endif() install(DIRECTORY include/clang-c + COMPONENT libclang DESTINATION include FILES_MATCHING PATTERN "*.h" diff --git a/tools/c-index-test/CMakeLists.txt b/tools/c-index-test/CMakeLists.txt index d0872fd2eff..7183854b39f 100644 --- a/tools/c-index-test/CMakeLists.txt +++ b/tools/c-index-test/CMakeLists.txt @@ -28,3 +28,12 @@ if (CLANG_HAVE_LIBXML) include_directories(SYSTEM ${LIBXML2_INCLUDE_DIR}) target_link_libraries(c-index-test ${LIBXML2_LIBRARIES}) endif() + +install(TARGETS c-index-test + RUNTIME DESTINATION local/bin + COMPONENT c-index-test) +add_custom_target(install-c-index-test + DEPENDS c-index-test + COMMAND "${CMAKE_COMMAND}" + -DCMAKE_INSTALL_COMPONENT=c-index-test + -P "${CMAKE_BINARY_DIR}/cmake_install.cmake") diff --git a/tools/libclang/CMakeLists.txt b/tools/libclang/CMakeLists.txt index 07fc17e0a4e..bd8368a82c9 100644 --- a/tools/libclang/CMakeLists.txt +++ b/tools/libclang/CMakeLists.txt @@ -103,18 +103,16 @@ if(ENABLE_SHARED) PROPERTIES VERSION ${LIBCLANG_LIBRARY_VERSION} DEFINE_SYMBOL _CINDEX_LIB_) - else() - set_target_properties(libclang - PROPERTIES - VERSION ${LIBCLANG_LIBRARY_VERSION} - DEFINE_SYMBOL _CINDEX_LIB_) - endif() - - if(APPLE) + elseif(APPLE) set(LIBCLANG_LINK_FLAGS " -Wl,-compatibility_version -Wl,1") set(LIBCLANG_LINK_FLAGS "${LIBCLANG_LINK_FLAGS} -Wl,-current_version -Wl,${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}") set_property(TARGET libclang APPEND_STRING PROPERTY LINK_FLAGS ${LIBCLANG_LINK_FLAGS}) + else() + set_target_properties(libclang + PROPERTIES + VERSION ${LIBCLANG_LIBRARY_VERSION} + DEFINE_SYMBOL _CINDEX_LIB_) endif() endif() From 29edf503e634fe3a1e6551dc1babdd22880b196a Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Thu, 12 Nov 2015 13:58:44 -0800 Subject: [PATCH 005/742] Revert "[CMake] Setup an install component for libclang and c-index-test." This reverts commit 6d317b334ce13fcc53c51dfa0f95d50fa60b2ba5. It is breaking the build. --- CMakeLists.txt | 17 ++--------------- tools/c-index-test/CMakeLists.txt | 9 --------- tools/libclang/CMakeLists.txt | 14 ++++++++------ 3 files changed, 10 insertions(+), 30 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a78c6ca909..3aa0baeffae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -354,7 +354,7 @@ endmacro() macro(add_clang_library name) cmake_parse_arguments(ARG - "SHARED" + "" "" "ADDITIONAL_HEADERS" ${ARGN}) @@ -390,29 +390,17 @@ macro(add_clang_library name) ${ARG_ADDITIONAL_HEADERS} # It may contain unparsed unknown args. ) endif() - if(ARG_SHARED) - set(ARG_ENABLE_SHARED SHARED) - endif() - llvm_add_library(${name} ${ARG_ENABLE_SHARED} ${ARG_UNPARSED_ARGUMENTS} ${srcs}) + llvm_add_library(${name} ${ARG_UNPARSED_ARGUMENTS} ${srcs}) if(TARGET ${name}) target_link_libraries(${name} ${cmake_2_8_12_INTERFACE} ${LLVM_COMMON_LIBS}) if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY OR ${name} STREQUAL "libclang") install(TARGETS ${name} - COMPONENT ${name} EXPORT ClangTargets LIBRARY DESTINATION lib${LLVM_LIBDIR_SUFFIX} ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX} RUNTIME DESTINATION bin) - - if (${ARG_SHARED} AND NOT CMAKE_CONFIGURATION_TYPES) - add_custom_target(install-${name} - DEPENDS ${name} - COMMAND "${CMAKE_COMMAND}" - -DCMAKE_INSTALL_COMPONENT=${name} - -P "${CMAKE_BINARY_DIR}/cmake_install.cmake") - endif() endif() set_property(GLOBAL APPEND PROPERTY CLANG_EXPORTS ${name}) else() @@ -463,7 +451,6 @@ if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) endif() install(DIRECTORY include/clang-c - COMPONENT libclang DESTINATION include FILES_MATCHING PATTERN "*.h" diff --git a/tools/c-index-test/CMakeLists.txt b/tools/c-index-test/CMakeLists.txt index 7183854b39f..d0872fd2eff 100644 --- a/tools/c-index-test/CMakeLists.txt +++ b/tools/c-index-test/CMakeLists.txt @@ -28,12 +28,3 @@ if (CLANG_HAVE_LIBXML) include_directories(SYSTEM ${LIBXML2_INCLUDE_DIR}) target_link_libraries(c-index-test ${LIBXML2_LIBRARIES}) endif() - -install(TARGETS c-index-test - RUNTIME DESTINATION local/bin - COMPONENT c-index-test) -add_custom_target(install-c-index-test - DEPENDS c-index-test - COMMAND "${CMAKE_COMMAND}" - -DCMAKE_INSTALL_COMPONENT=c-index-test - -P "${CMAKE_BINARY_DIR}/cmake_install.cmake") diff --git a/tools/libclang/CMakeLists.txt b/tools/libclang/CMakeLists.txt index bd8368a82c9..07fc17e0a4e 100644 --- a/tools/libclang/CMakeLists.txt +++ b/tools/libclang/CMakeLists.txt @@ -103,16 +103,18 @@ if(ENABLE_SHARED) PROPERTIES VERSION ${LIBCLANG_LIBRARY_VERSION} DEFINE_SYMBOL _CINDEX_LIB_) - elseif(APPLE) - set(LIBCLANG_LINK_FLAGS " -Wl,-compatibility_version -Wl,1") - set(LIBCLANG_LINK_FLAGS "${LIBCLANG_LINK_FLAGS} -Wl,-current_version -Wl,${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}") - - set_property(TARGET libclang APPEND_STRING PROPERTY - LINK_FLAGS ${LIBCLANG_LINK_FLAGS}) else() set_target_properties(libclang PROPERTIES VERSION ${LIBCLANG_LIBRARY_VERSION} DEFINE_SYMBOL _CINDEX_LIB_) endif() + + if(APPLE) + set(LIBCLANG_LINK_FLAGS " -Wl,-compatibility_version -Wl,1") + set(LIBCLANG_LINK_FLAGS "${LIBCLANG_LINK_FLAGS} -Wl,-current_version -Wl,${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}") + + set_property(TARGET libclang APPEND_STRING PROPERTY + LINK_FLAGS ${LIBCLANG_LINK_FLAGS}) + endif() endif() From e0c82b6b8fe86c7a5fd6a0da4aefe7fcf73d7c3a Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Fri, 13 Nov 2015 08:44:38 -0800 Subject: [PATCH 006/742] Revert "Revert "[CMake] Setup an install component for libclang and c-index-test."" This reverts commit 29edf503e634fe3a1e6551dc1babdd22880b196a. Turns out the problem was that the commit that was reverted was cherry-picked into oss, but not internally. --- CMakeLists.txt | 17 +++++++++++++++-- tools/c-index-test/CMakeLists.txt | 9 +++++++++ tools/libclang/CMakeLists.txt | 14 ++++++-------- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3aa0baeffae..2a78c6ca909 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -354,7 +354,7 @@ endmacro() macro(add_clang_library name) cmake_parse_arguments(ARG - "" + "SHARED" "" "ADDITIONAL_HEADERS" ${ARGN}) @@ -390,17 +390,29 @@ macro(add_clang_library name) ${ARG_ADDITIONAL_HEADERS} # It may contain unparsed unknown args. ) endif() - llvm_add_library(${name} ${ARG_UNPARSED_ARGUMENTS} ${srcs}) + if(ARG_SHARED) + set(ARG_ENABLE_SHARED SHARED) + endif() + llvm_add_library(${name} ${ARG_ENABLE_SHARED} ${ARG_UNPARSED_ARGUMENTS} ${srcs}) if(TARGET ${name}) target_link_libraries(${name} ${cmake_2_8_12_INTERFACE} ${LLVM_COMMON_LIBS}) if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY OR ${name} STREQUAL "libclang") install(TARGETS ${name} + COMPONENT ${name} EXPORT ClangTargets LIBRARY DESTINATION lib${LLVM_LIBDIR_SUFFIX} ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX} RUNTIME DESTINATION bin) + + if (${ARG_SHARED} AND NOT CMAKE_CONFIGURATION_TYPES) + add_custom_target(install-${name} + DEPENDS ${name} + COMMAND "${CMAKE_COMMAND}" + -DCMAKE_INSTALL_COMPONENT=${name} + -P "${CMAKE_BINARY_DIR}/cmake_install.cmake") + endif() endif() set_property(GLOBAL APPEND PROPERTY CLANG_EXPORTS ${name}) else() @@ -451,6 +463,7 @@ if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) endif() install(DIRECTORY include/clang-c + COMPONENT libclang DESTINATION include FILES_MATCHING PATTERN "*.h" diff --git a/tools/c-index-test/CMakeLists.txt b/tools/c-index-test/CMakeLists.txt index d0872fd2eff..7183854b39f 100644 --- a/tools/c-index-test/CMakeLists.txt +++ b/tools/c-index-test/CMakeLists.txt @@ -28,3 +28,12 @@ if (CLANG_HAVE_LIBXML) include_directories(SYSTEM ${LIBXML2_INCLUDE_DIR}) target_link_libraries(c-index-test ${LIBXML2_LIBRARIES}) endif() + +install(TARGETS c-index-test + RUNTIME DESTINATION local/bin + COMPONENT c-index-test) +add_custom_target(install-c-index-test + DEPENDS c-index-test + COMMAND "${CMAKE_COMMAND}" + -DCMAKE_INSTALL_COMPONENT=c-index-test + -P "${CMAKE_BINARY_DIR}/cmake_install.cmake") diff --git a/tools/libclang/CMakeLists.txt b/tools/libclang/CMakeLists.txt index 07fc17e0a4e..bd8368a82c9 100644 --- a/tools/libclang/CMakeLists.txt +++ b/tools/libclang/CMakeLists.txt @@ -103,18 +103,16 @@ if(ENABLE_SHARED) PROPERTIES VERSION ${LIBCLANG_LIBRARY_VERSION} DEFINE_SYMBOL _CINDEX_LIB_) - else() - set_target_properties(libclang - PROPERTIES - VERSION ${LIBCLANG_LIBRARY_VERSION} - DEFINE_SYMBOL _CINDEX_LIB_) - endif() - - if(APPLE) + elseif(APPLE) set(LIBCLANG_LINK_FLAGS " -Wl,-compatibility_version -Wl,1") set(LIBCLANG_LINK_FLAGS "${LIBCLANG_LINK_FLAGS} -Wl,-current_version -Wl,${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}") set_property(TARGET libclang APPEND_STRING PROPERTY LINK_FLAGS ${LIBCLANG_LINK_FLAGS}) + else() + set_target_properties(libclang + PROPERTIES + VERSION ${LIBCLANG_LIBRARY_VERSION} + DEFINE_SYMBOL _CINDEX_LIB_) endif() endif() From ecab2fcf6c5fece3b6a16b66441b4947a98ebb6f Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 13 Nov 2015 10:29:43 -0800 Subject: [PATCH 007/742] Annoint myself as code owner of Swift language-specific changes. --- CODE_OWNERS.TXT | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODE_OWNERS.TXT b/CODE_OWNERS.TXT index 21962ca3382..9fbc5c7567a 100644 --- a/CODE_OWNERS.TXT +++ b/CODE_OWNERS.TXT @@ -27,7 +27,7 @@ D: Debug Information, autotools/configure/make build, inline assembly N: Doug Gregor E: dgregor@apple.com -D: Emeritus owner +D: Emeritus owner, Swift language-specific changes N: Reid Kleckner E: rnk@google.com From 6ebf6ed4f784c214ea1e56983f0ed523f6c33697 Mon Sep 17 00:00:00 2001 From: Ismail Donmez Date: Thu, 12 Nov 2015 13:47:35 +0000 Subject: [PATCH 008/742] Fix c-index-test install path git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@252890 91177308-0d34-0410-b5e6-96231b3b80d8 --- tools/c-index-test/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/c-index-test/CMakeLists.txt b/tools/c-index-test/CMakeLists.txt index 7183854b39f..87db20d7366 100644 --- a/tools/c-index-test/CMakeLists.txt +++ b/tools/c-index-test/CMakeLists.txt @@ -30,7 +30,7 @@ if (CLANG_HAVE_LIBXML) endif() install(TARGETS c-index-test - RUNTIME DESTINATION local/bin + RUNTIME DESTINATION bin COMPONENT c-index-test) add_custom_target(install-c-index-test DEPENDS c-index-test From f4e7d8bceb71292c9291e594bde1f0b649f335cc Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Fri, 13 Nov 2015 01:46:18 +0000 Subject: [PATCH 009/742] [CMake] If 'INTERNAL_INSTALL_PREFIX' is set, use it for determining the install destination of c-index-test and the libclang headers. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@253001 91177308-0d34-0410-b5e6-96231b3b80d8 --- CMakeLists.txt | 18 ++++++++++++++++-- tools/c-index-test/CMakeLists.txt | 21 +++++++++++++++------ 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a78c6ca909..bbc87510de3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -462,14 +462,28 @@ if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) ) endif() +if(INTERNAL_INSTALL_PREFIX) + set(LIBCLANG_HEADERS_INSTALL_DESTINATION "${INTERNAL_INSTALL_PREFIX}/include") +else() + set(LIBCLANG_HEADERS_INSTALL_DESTINATION include) +endif() + install(DIRECTORY include/clang-c - COMPONENT libclang - DESTINATION include + COMPONENT libclang-headers + DESTINATION "${LIBCLANG_HEADERS_INSTALL_DESTINATION}" FILES_MATCHING PATTERN "*.h" PATTERN ".svn" EXCLUDE ) +if (NOT CMAKE_CONFIGURATION_TYPES) # don't add this for IDE's. + add_custom_target(install-libclang-headers + DEPENDS + COMMAND "${CMAKE_COMMAND}" + -DCMAKE_INSTALL_COMPONENT=libclang-headers + -P "${CMAKE_BINARY_DIR}/cmake_install.cmake") +endif() + add_definitions( -D_GNU_SOURCE ) option(CLANG_ENABLE_ARCMT "Build ARCMT." ON) diff --git a/tools/c-index-test/CMakeLists.txt b/tools/c-index-test/CMakeLists.txt index 87db20d7366..64efb1135f8 100644 --- a/tools/c-index-test/CMakeLists.txt +++ b/tools/c-index-test/CMakeLists.txt @@ -29,11 +29,20 @@ if (CLANG_HAVE_LIBXML) target_link_libraries(c-index-test ${LIBXML2_LIBRARIES}) endif() +if(INTERNAL_INSTALL_PREFIX) + set(INSTALL_DESTINATION "${INTERNAL_INSTALL_PREFIX}/bin") +else() + set(INSTALL_DESTINATION bin) +endif() + install(TARGETS c-index-test - RUNTIME DESTINATION bin + RUNTIME DESTINATION "${INSTALL_DESTINATION}" COMPONENT c-index-test) -add_custom_target(install-c-index-test - DEPENDS c-index-test - COMMAND "${CMAKE_COMMAND}" - -DCMAKE_INSTALL_COMPONENT=c-index-test - -P "${CMAKE_BINARY_DIR}/cmake_install.cmake") + +if (NOT CMAKE_CONFIGURATION_TYPES) # don't add this for IDE's. + add_custom_target(install-c-index-test + DEPENDS c-index-test + COMMAND "${CMAKE_COMMAND}" + -DCMAKE_INSTALL_COMPONENT=c-index-test + -P "${CMAKE_BINARY_DIR}/cmake_install.cmake") +endif() From 687204c1f1e4ae5094914355bbf12d28f406ee44 Mon Sep 17 00:00:00 2001 From: Juergen Ributzka Date: Fri, 13 Nov 2015 19:08:07 +0000 Subject: [PATCH 010/742] Fix auto-link for text-based dynamic library SDKs. When linking against text-based dynamic library SDKs the library name of a framework has now more than one possible filename extensions. This fix tests for both possible extensions (none, and .tbd). This fixes rdar://problem/20609975 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@253060 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Lex/ModuleMap.cpp | 15 ++++++++++++--- .../AutolinkTBD.framework/AutolinkTBD.tbd | 1 + .../AutolinkTBD.framework/Headers/AutolinkTBD.h | 1 + test/Modules/autolinkTBD.m | 17 +++++++++++++++++ 4 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 test/Modules/Inputs/AutolinkTBD.framework/AutolinkTBD.tbd create mode 100644 test/Modules/Inputs/AutolinkTBD.framework/Headers/AutolinkTBD.h create mode 100644 test/Modules/autolinkTBD.m diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp index d997aedabc0..e14f8766ab1 100644 --- a/lib/Lex/ModuleMap.cpp +++ b/lib/Lex/ModuleMap.cpp @@ -596,9 +596,18 @@ static void inferFrameworkLink(Module *Mod, const DirectoryEntry *FrameworkDir, SmallString<128> LibName; LibName += FrameworkDir->getName(); llvm::sys::path::append(LibName, Mod->Name); - if (FileMgr.getFile(LibName)) { - Mod->LinkLibraries.push_back(Module::LinkLibrary(Mod->Name, - /*IsFramework=*/true)); + + // The library name of a framework has more than one possible extension since + // the introduction of the text-based dynamic library format. We need to check + // for both before we give up. + static const char *frameworkExtensions[] = {"", ".tbd"}; + for (const auto *extension : frameworkExtensions) { + llvm::sys::path::replace_extension(LibName, extension); + if (FileMgr.getFile(LibName)) { + Mod->LinkLibraries.push_back(Module::LinkLibrary(Mod->Name, + /*IsFramework=*/true)); + return; + } } } diff --git a/test/Modules/Inputs/AutolinkTBD.framework/AutolinkTBD.tbd b/test/Modules/Inputs/AutolinkTBD.framework/AutolinkTBD.tbd new file mode 100644 index 00000000000..4aa0f85d0d5 --- /dev/null +++ b/test/Modules/Inputs/AutolinkTBD.framework/AutolinkTBD.tbd @@ -0,0 +1 @@ +empty file - clang only needs to check if it exists. diff --git a/test/Modules/Inputs/AutolinkTBD.framework/Headers/AutolinkTBD.h b/test/Modules/Inputs/AutolinkTBD.framework/Headers/AutolinkTBD.h new file mode 100644 index 00000000000..cf790ac3eab --- /dev/null +++ b/test/Modules/Inputs/AutolinkTBD.framework/Headers/AutolinkTBD.h @@ -0,0 +1 @@ +extern int foo(); diff --git a/test/Modules/autolinkTBD.m b/test/Modules/autolinkTBD.m new file mode 100644 index 00000000000..6107952c3b9 --- /dev/null +++ b/test/Modules/autolinkTBD.m @@ -0,0 +1,17 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -emit-llvm -o - -fmodules-cache-path=%t -fmodules -fimplicit-module-maps -F %S/Inputs %s | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -fno-autolink -o - -fmodules-cache-path=%t -fmodules -fimplicit-module-maps -F %S/Inputs %s | FileCheck --check-prefix=CHECK-AUTOLINK-DISABLED %s + +@import AutolinkTBD; + +int f() { + return foo(); +} + +// CHECK: !llvm.module.flags = !{{{.*}}} +// CHECK: !{{[0-9]+}} = !{i32 6, !"Linker Options", ![[AUTOLINK_OPTIONS:[0-9]+]]} +// CHECK: ![[AUTOLINK_OPTIONS]] = !{![[AUTOLINK_FRAMEWORK:[0-9]+]]} +// CHECK: ![[AUTOLINK_FRAMEWORK]] = !{!"-framework", !"AutolinkTBD"} + +// CHECK-AUTOLINK-DISABLED: !llvm.module.flags +// CHECK-AUTOLINK-DISABLED-NOT: "Linker Options" From d30f4c42be16b3a89eddd772c6f0333a7fd814a6 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Fri, 13 Nov 2015 22:41:14 +0000 Subject: [PATCH 011/742] [CMake] Don't install c-index-test when LLVM_INSTALL_TOOLCHAIN_ONLY=ON. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@253099 91177308-0d34-0410-b5e6-96231b3b80d8 --- tools/c-index-test/CMakeLists.txt | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/tools/c-index-test/CMakeLists.txt b/tools/c-index-test/CMakeLists.txt index 64efb1135f8..c78a42ffe8e 100644 --- a/tools/c-index-test/CMakeLists.txt +++ b/tools/c-index-test/CMakeLists.txt @@ -29,20 +29,22 @@ if (CLANG_HAVE_LIBXML) target_link_libraries(c-index-test ${LIBXML2_LIBRARIES}) endif() -if(INTERNAL_INSTALL_PREFIX) - set(INSTALL_DESTINATION "${INTERNAL_INSTALL_PREFIX}/bin") -else() - set(INSTALL_DESTINATION bin) -endif() +if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) + if(INTERNAL_INSTALL_PREFIX) + set(INSTALL_DESTINATION "${INTERNAL_INSTALL_PREFIX}/bin") + else() + set(INSTALL_DESTINATION bin) + endif() -install(TARGETS c-index-test - RUNTIME DESTINATION "${INSTALL_DESTINATION}" - COMPONENT c-index-test) + install(TARGETS c-index-test + RUNTIME DESTINATION "${INSTALL_DESTINATION}" + COMPONENT c-index-test) -if (NOT CMAKE_CONFIGURATION_TYPES) # don't add this for IDE's. - add_custom_target(install-c-index-test - DEPENDS c-index-test - COMMAND "${CMAKE_COMMAND}" - -DCMAKE_INSTALL_COMPONENT=c-index-test - -P "${CMAKE_BINARY_DIR}/cmake_install.cmake") + if (NOT CMAKE_CONFIGURATION_TYPES) # don't add this for IDE's. + add_custom_target(install-c-index-test + DEPENDS c-index-test + COMMAND "${CMAKE_COMMAND}" + -DCMAKE_INSTALL_COMPONENT=c-index-test + -P "${CMAKE_BINARY_DIR}/cmake_install.cmake") + endif() endif() From 66c9a1c3212a64428edb1fa7a8b8a748d3a91812 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Fri, 20 Nov 2015 02:24:03 +0000 Subject: [PATCH 012/742] [CMake] Add a specific 'install-clang-headers' target. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@253636 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Headers/CMakeLists.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/Headers/CMakeLists.txt b/lib/Headers/CMakeLists.txt index 3de7874864b..8c06b27b7be 100644 --- a/lib/Headers/CMakeLists.txt +++ b/lib/Headers/CMakeLists.txt @@ -101,5 +101,14 @@ set_target_properties(clang-headers PROPERTIES FOLDER "Misc") install( FILES ${files} ${CMAKE_CURRENT_BINARY_DIR}/arm_neon.h + COMPONENT clang-headers PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ DESTINATION lib${LLVM_LIBDIR_SUFFIX}/clang/${CLANG_VERSION}/include) + +if (NOT CMAKE_CONFIGURATION_TYPES) # don't add this for IDE's. + add_custom_target(install-clang-headers + DEPENDS + COMMAND "${CMAKE_COMMAND}" + -DCMAKE_INSTALL_COMPONENT=clang-headers + -P "${CMAKE_BINARY_DIR}/cmake_install.cmake") +endif() From 4deb154edccff8545d801c567628464082e7aeb9 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Fri, 20 Nov 2015 03:36:21 +0000 Subject: [PATCH 013/742] [libclang] Make sure to use the raw module format for libclang parsing. Fixes crash when passing '-gmodules' in the compiler options. rdar://23588717 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@253645 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Frontend/ASTUnit.h | 3 +++ lib/Frontend/ASTUnit.cpp | 4 ++++ test/Index/annotate-module.m | 2 ++ test/Index/complete-modules.m | 1 + test/Index/index-module.m | 2 ++ tools/libclang/CIndex.cpp | 4 +++- tools/libclang/Indexing.cpp | 4 ++++ 7 files changed, 19 insertions(+), 1 deletion(-) diff --git a/include/clang/Frontend/ASTUnit.h b/include/clang/Frontend/ASTUnit.h index 7d9d1210383..d326a53dc2a 100644 --- a/include/clang/Frontend/ASTUnit.h +++ b/include/clang/Frontend/ASTUnit.h @@ -827,6 +827,8 @@ class ASTUnit : public ModuleLoader { /// /// \param ResourceFilesPath - The path to the compiler resource files. /// + /// \param ModuleFormat - If provided, uses the specific module format. + /// /// \param ErrAST - If non-null and parsing failed without any AST to return /// (e.g. because the PCH could not be loaded), this accepts the ASTUnit /// mainly to allow the caller to see the diagnostics. @@ -845,6 +847,7 @@ class ASTUnit : public ModuleLoader { bool IncludeBriefCommentsInCodeCompletion = false, bool AllowPCHWithCompilerErrors = false, bool SkipFunctionBodies = false, bool UserFilesAreVolatile = false, bool ForSerialization = false, + llvm::Optional ModuleFormat = llvm::None, std::unique_ptr *ErrAST = nullptr); /// \brief Reparse the source files using the same command-line options that diff --git a/lib/Frontend/ASTUnit.cpp b/lib/Frontend/ASTUnit.cpp index 6b89447305f..0d5dd536589 100644 --- a/lib/Frontend/ASTUnit.cpp +++ b/lib/Frontend/ASTUnit.cpp @@ -1934,6 +1934,7 @@ ASTUnit *ASTUnit::LoadFromCommandLine( bool CacheCodeCompletionResults, bool IncludeBriefCommentsInCodeCompletion, bool AllowPCHWithCompilerErrors, bool SkipFunctionBodies, bool UserFilesAreVolatile, bool ForSerialization, + llvm::Optional ModuleFormat, std::unique_ptr *ErrAST) { assert(Diags.get() && "no DiagnosticsEngine was provided"); @@ -1967,6 +1968,9 @@ ASTUnit *ASTUnit::LoadFromCommandLine( CI->getFrontendOpts().SkipFunctionBodies = SkipFunctionBodies; + if (ModuleFormat) + CI->getHeaderSearchOpts().ModuleFormat = ModuleFormat.getValue(); + // Create the AST unit. std::unique_ptr AST; AST.reset(new ASTUnit(false)); diff --git a/test/Index/annotate-module.m b/test/Index/annotate-module.m index 456a192f425..24dce3f290b 100644 --- a/test/Index/annotate-module.m +++ b/test/Index/annotate-module.m @@ -6,6 +6,8 @@ // RUN: rm -rf %t.cache // RUN: c-index-test -test-annotate-tokens=%s:2:1:5:1 %s -fmodules-cache-path=%t.cache -fmodules -F %S/../Modules/Inputs \ // RUN: | FileCheck %s +// RUN: c-index-test -test-annotate-tokens=%s:2:1:5:1 %s -fmodules-cache-path=%t.cache -fmodules -gmodules -F %S/../Modules/Inputs \ +// RUN: | FileCheck %s // CHECK: Punctuation: "#" [2:1 - 2:2] inclusion directive=[[INC_DIR:DependsOnModule[/\\]DependsOnModule\.h \(.*/Modules/Inputs/DependsOnModule\.framework[/\\]Headers[/\\]DependsOnModule.h\)]] // CHECK-NEXT: Identifier: "include" [2:2 - 2:9] inclusion directive=[[INC_DIR]] diff --git a/test/Index/complete-modules.m b/test/Index/complete-modules.m index 175cf3db2c7..21f0e4bafe8 100644 --- a/test/Index/complete-modules.m +++ b/test/Index/complete-modules.m @@ -5,6 +5,7 @@ // RUN: rm -rf %t // RUN: c-index-test -code-completion-at=%s:4:9 -fmodules-cache-path=%t -fmodules -F %S/Inputs/Frameworks -I %S/Inputs/Headers %s | FileCheck -check-prefix=CHECK-TOP-LEVEL %s +// RUN: c-index-test -code-completion-at=%s:4:9 -fmodules-cache-path=%t -fmodules -gmodules -F %S/Inputs/Frameworks -I %S/Inputs/Headers %s | FileCheck -check-prefix=CHECK-TOP-LEVEL %s // CHECK-TOP-LEVEL: ModuleImport:{TypedText Framework} (50) // CHECK-TOP-LEVEL: ModuleImport:{TypedText LibA} (50) // CHECK-TOP-LEVEL: ModuleImport:{TypedText nested} (50) diff --git a/test/Index/index-module.m b/test/Index/index-module.m index a973e91af83..ff512592b67 100644 --- a/test/Index/index-module.m +++ b/test/Index/index-module.m @@ -6,6 +6,8 @@ // RUN: rm -rf %t.cache // RUN: c-index-test -index-file %s -fmodules-cache-path=%t.cache -fmodules -F %S/../Modules/Inputs \ // RUN: -Xclang -fdisable-module-hash | FileCheck %s +// RUN: c-index-test -index-file %s -fmodules-cache-path=%t.cache -fmodules -gmodules -F %S/../Modules/Inputs \ +// RUN: -Xclang -fdisable-module-hash | FileCheck %s // CHECK-NOT: [indexDeclaration] // CHECK: [ppIncludedFile]: {{.*}}/Modules/Inputs/DependsOnModule.framework{{[/\\]}}Headers{{[/\\]}}DependsOnModule.h | name: "DependsOnModule/DependsOnModule.h" | hash loc: 2:1 | isImport: 0 | isAngled: 1 | isModule: 1 diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index ac6f98de40e..5fa2566a8ed 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -3112,7 +3112,9 @@ clang_parseTranslationUnit_Impl(CXIndex CIdx, const char *source_filename, /*RemappedFilesKeepOriginalName=*/true, PrecompilePreamble, TUKind, CacheCodeCompletionResults, IncludeBriefCommentsInCodeCompletion, /*AllowPCHWithCompilerErrors=*/true, SkipFunctionBodies, - /*UserFilesAreVolatile=*/true, ForSerialization, &ErrUnit)); + /*UserFilesAreVolatile=*/true, ForSerialization, + CXXIdx->getPCHContainerOperations()->getRawReader().getFormat(), + &ErrUnit)); // Early failures in LoadFromCommandLine may return with ErrUnit unset. if (!Unit && !ErrUnit) diff --git a/tools/libclang/Indexing.cpp b/tools/libclang/Indexing.cpp index 8a0498497e0..2b8daebdfa0 100644 --- a/tools/libclang/Indexing.cpp +++ b/tools/libclang/Indexing.cpp @@ -566,6 +566,10 @@ static CXErrorCode clang_indexSourceFile_Impl( if (index_options & CXIndexOpt_SuppressWarnings) CInvok->getDiagnosticOpts().IgnoreWarnings = true; + // Make sure to use the raw module format. + CInvok->getHeaderSearchOpts().ModuleFormat = + CXXIdx->getPCHContainerOperations()->getRawReader().getFormat(); + ASTUnit *Unit = ASTUnit::create(CInvok.get(), Diags, CaptureDiagnostics, /*UserFilesAreVolatile=*/true); if (!Unit) From 587b76f2f66dc073cf8d47b59b896edd54da31d5 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Sat, 5 Dec 2015 11:51:53 -0800 Subject: [PATCH 014/742] Add legal notice for pull requests and reference to contribution guidelines. GitHub will look for this file when a new pull request is opened and offer it to the user. --- CONTRIBUTING.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000000..a0c1644fc66 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,14 @@ +By submitting a pull request, you represent that you have the right to license +your contribution to Apple and the community, and agree by submitting the patch +that your contributions are licensed under the [Swift +license](https://swift.org/LICENSE.txt). + +--- + +Changes to this repository follow special considerations as described on +Swift.org under "[LLVM and Swift](https://swift.org/contributing/#llvm-and-swift)". +Please make sure your change is appropriate for this repository. + +Before submitting a pull request, please make sure you have tested your +changes and that they follow the Swift project [guidelines for contributing +code](https://swift.org/contributing/#contributing-code). From 53d04af5ce14310813e7f4a5db9a949e70829f72 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Tue, 8 Dec 2015 22:43:32 +0000 Subject: [PATCH 015/742] Module file extensions: pass a Sema through to the extension writer. Module file extensions are likely to need access to Sema/Preprocessor/ASTContext, and cannot get it through other sources. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@255065 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Serialization/ASTWriter.h | 3 ++- include/clang/Serialization/ModuleFileExtension.h | 8 +++++--- lib/Frontend/TestModuleFileExtension.cpp | 1 + lib/Frontend/TestModuleFileExtension.h | 3 ++- lib/Serialization/ASTWriter.cpp | 7 ++++--- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/include/clang/Serialization/ASTWriter.h b/include/clang/Serialization/ASTWriter.h index 86dbceef4c4..ed345472fc7 100644 --- a/include/clang/Serialization/ASTWriter.h +++ b/include/clang/Serialization/ASTWriter.h @@ -551,7 +551,8 @@ class ASTWriter : public ASTDeserializationListener, void WriteObjCCategories(); void WriteLateParsedTemplates(Sema &SemaRef); void WriteOptimizePragmaOptions(Sema &SemaRef); - void WriteModuleFileExtension(ModuleFileExtensionWriter &Writer); + void WriteModuleFileExtension(Sema &SemaRef, + ModuleFileExtensionWriter &Writer); unsigned DeclParmVarAbbrev; unsigned DeclContextLexicalAbbrev; diff --git a/include/clang/Serialization/ModuleFileExtension.h b/include/clang/Serialization/ModuleFileExtension.h index a8f8386e649..ba2e2fd0d9f 100644 --- a/include/clang/Serialization/ModuleFileExtension.h +++ b/include/clang/Serialization/ModuleFileExtension.h @@ -25,7 +25,8 @@ namespace clang { class ASTReader; class ASTWriter; - +class Sema; + namespace serialization { class ModuleFile; } // end namespace serialization @@ -79,7 +80,7 @@ class ModuleFileExtension : public llvm::RefCountedBase { /// The default implementation of this function simply returns the /// hash code as given, so the presence/absence of this extension /// does not distinguish module files. - virtual llvm::hash_code hashExtension(llvm::hash_code Code) const; + virtual llvm::hash_code hashExtension(llvm::hash_code c) const; /// Create a new module file extension writer, which will be /// responsible for writing the extension contents into a particular @@ -120,7 +121,8 @@ class ModuleFileExtensionWriter { /// Responsible for writing the contents of the extension into the /// given stream. All of the contents should be written into custom /// records with IDs >= FIRST_EXTENSION_RECORD_ID. - virtual void writeExtensionContents(llvm::BitstreamWriter &Stream) = 0; + virtual void writeExtensionContents(Sema &SemaRef, + llvm::BitstreamWriter &Stream) = 0; }; /// Abstract base class that reads a module file extension block from diff --git a/lib/Frontend/TestModuleFileExtension.cpp b/lib/Frontend/TestModuleFileExtension.cpp index c020468ce0f..d1b20c4a80b 100644 --- a/lib/Frontend/TestModuleFileExtension.cpp +++ b/lib/Frontend/TestModuleFileExtension.cpp @@ -19,6 +19,7 @@ using namespace clang::serialization; TestModuleFileExtension::Writer::~Writer() { } void TestModuleFileExtension::Writer::writeExtensionContents( + Sema &SemaRef, llvm::BitstreamWriter &Stream) { using namespace llvm; diff --git a/lib/Frontend/TestModuleFileExtension.h b/lib/Frontend/TestModuleFileExtension.h index 2a7245463cf..41f3ca9f05f 100644 --- a/lib/Frontend/TestModuleFileExtension.h +++ b/lib/Frontend/TestModuleFileExtension.h @@ -30,7 +30,8 @@ class TestModuleFileExtension : public ModuleFileExtension { Writer(ModuleFileExtension *Ext) : ModuleFileExtensionWriter(Ext) { } ~Writer() override; - void writeExtensionContents(llvm::BitstreamWriter &Stream) override; + void writeExtensionContents(Sema &SemaRef, + llvm::BitstreamWriter &Stream) override; }; class Reader : public ModuleFileExtensionReader { diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 5508b6eceac..9b0a9b1cb72 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -3881,7 +3881,8 @@ void ASTWriter::WriteOptimizePragmaOptions(Sema &SemaRef) { Stream.EmitRecord(OPTIMIZE_PRAGMA_OPTIONS, Record); } -void ASTWriter::WriteModuleFileExtension(ModuleFileExtensionWriter &Writer) { +void ASTWriter::WriteModuleFileExtension(Sema &SemaRef, + ModuleFileExtensionWriter &Writer) { // Enter the extension block. Stream.EnterSubblock(EXTENSION_BLOCK_ID, 4); @@ -3909,7 +3910,7 @@ void ASTWriter::WriteModuleFileExtension(ModuleFileExtensionWriter &Writer) { Stream.EmitRecordWithBlob(Abbrev, Record, Buffer); // Emit the contents of the extension block. - Writer.writeExtensionContents(Stream); + Writer.writeExtensionContents(SemaRef, Stream); // Exit the extension block. Stream.ExitBlock(); @@ -4576,7 +4577,7 @@ uint64_t ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot, // Write the module file extension blocks. for (const auto &ExtWriter : ModuleFileExtensionWriters) - WriteModuleFileExtension(*ExtWriter); + WriteModuleFileExtension(SemaRef, *ExtWriter); return Signature; } From 4b3278bec5a58a536e91f0c5a2a768b04f433be1 Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Thu, 10 Dec 2015 23:41:39 +0000 Subject: [PATCH 016/742] [VFS] Fix status() of opened redirected file Make RedirectedFileSystem::openFilForRead(path)->status() the same as RedirectedFileSystem::status(path). Previously we would just get the status of the underlying real file, which would not have the IsVFSMapped bit set. This fixes rebuilding a module that has an include that is relative to the includer where we will lookup the real path of that file before we lookup the VFS location. rdar://problem/23640339 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@255312 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Basic/VirtualFileSystem.cpp | 50 +++++++++++++---------- test/VFS/Inputs/public_header.h | 1 + test/VFS/Inputs/public_header3.h | 1 + test/VFS/Inputs/vfsoverlay.yaml | 4 +- test/VFS/real-path-found-first.m | 1 + unittests/Basic/VirtualFileSystemTest.cpp | 27 +++++++++++- 6 files changed, 60 insertions(+), 24 deletions(-) create mode 100644 test/VFS/Inputs/public_header3.h diff --git a/lib/Basic/VirtualFileSystem.cpp b/lib/Basic/VirtualFileSystem.cpp index 8acf0a997d5..3ea61c4221e 100644 --- a/lib/Basic/VirtualFileSystem.cpp +++ b/lib/Basic/VirtualFileSystem.cpp @@ -1268,20 +1268,27 @@ RedirectingFileSystem::lookupPath(sys::path::const_iterator Start, return make_error_code(llvm::errc::no_such_file_or_directory); } +static Status getRedirectedFileStatus(const Twine &Path, bool UseExternalNames, + Status ExternalStatus) { + Status S = ExternalStatus; + if (!UseExternalNames) + S = Status::copyWithNewName(S, Path.str()); + S.IsVFSMapped = true; + return S; +} + ErrorOr RedirectingFileSystem::status(const Twine &Path, Entry *E) { assert(E != nullptr); - std::string PathStr(Path.str()); if (auto *F = dyn_cast(E)) { ErrorOr S = ExternalFS->status(F->getExternalContentsPath()); assert(!S || S->getName() == F->getExternalContentsPath()); - if (S && !F->useExternalName(UseExternalNames)) - *S = Status::copyWithNewName(*S, PathStr); if (S) - S->IsVFSMapped = true; + return getRedirectedFileStatus(Path, F->useExternalName(UseExternalNames), + *S); return S; } else { // directory auto *DE = cast(E); - return Status::copyWithNewName(DE->getStatus(), PathStr); + return Status::copyWithNewName(DE->getStatus(), Path.str()); } } @@ -1293,22 +1300,17 @@ ErrorOr RedirectingFileSystem::status(const Twine &Path) { } namespace { -/// Provide a file wrapper that returns the external name when asked. -class NamedFileAdaptor : public File { +/// Provide a file wrapper with an overriden status. +class FileWithFixedStatus : public File { std::unique_ptr InnerFile; - std::string NewName; + Status S; public: - NamedFileAdaptor(std::unique_ptr InnerFile, std::string NewName) - : InnerFile(std::move(InnerFile)), NewName(std::move(NewName)) {} - - llvm::ErrorOr status() override { - auto InnerStatus = InnerFile->status(); - if (InnerStatus) - return Status::copyWithNewName(*InnerStatus, NewName); - return InnerStatus.getError(); - } - llvm::ErrorOr> + FileWithFixedStatus(std::unique_ptr InnerFile, Status S) + : InnerFile(std::move(InnerFile)), S(S) {} + + ErrorOr status() override { return S; } + ErrorOr> getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, bool IsVolatile) override { return InnerFile->getBuffer(Name, FileSize, RequiresNullTerminator, @@ -1332,11 +1334,15 @@ RedirectingFileSystem::openFileForRead(const Twine &Path) { if (!Result) return Result; - if (!F->useExternalName(UseExternalNames)) - return std::unique_ptr( - new NamedFileAdaptor(std::move(*Result), Path.str())); + auto ExternalStatus = (*Result)->status(); + if (!ExternalStatus) + return ExternalStatus.getError(); - return Result; + // FIXME: Update the status with the name and VFSMapped. + Status S = getRedirectedFileStatus(Path, F->useExternalName(UseExternalNames), + *ExternalStatus); + return std::unique_ptr( + llvm::make_unique(std::move(*Result), S)); } IntrusiveRefCntPtr diff --git a/test/VFS/Inputs/public_header.h b/test/VFS/Inputs/public_header.h index 09d9969d319..cc7bcb57eea 100644 --- a/test/VFS/Inputs/public_header.h +++ b/test/VFS/Inputs/public_header.h @@ -1,2 +1,3 @@ #import +#import "public_header3.h" // includer-relative void from_framework(void); diff --git a/test/VFS/Inputs/public_header3.h b/test/VFS/Inputs/public_header3.h new file mode 100644 index 00000000000..ac9deac6ab3 --- /dev/null +++ b/test/VFS/Inputs/public_header3.h @@ -0,0 +1 @@ +// public_header3.h diff --git a/test/VFS/Inputs/vfsoverlay.yaml b/test/VFS/Inputs/vfsoverlay.yaml index f395d45ee3b..504a1530d3c 100644 --- a/test/VFS/Inputs/vfsoverlay.yaml +++ b/test/VFS/Inputs/vfsoverlay.yaml @@ -22,7 +22,9 @@ { 'name': 'public_header.h', 'type': 'file', 'external-contents': 'INPUT_DIR/public_header.h' }, { 'name': 'public_header2.h', 'type': 'file', - 'external-contents': 'INPUT_DIR/public_header2.h' } + 'external-contents': 'INPUT_DIR/public_header2.h' }, + { 'name': 'public_header3.h', 'type': 'file', + 'external-contents': 'INPUT_DIR/public_header3.h' } ] } ] diff --git a/test/VFS/real-path-found-first.m b/test/VFS/real-path-found-first.m index 3b37a644bcb..5838aa36c45 100644 --- a/test/VFS/real-path-found-first.m +++ b/test/VFS/real-path-found-first.m @@ -70,5 +70,6 @@ #ifndef WITH_PREFIX #import // expected-warning{{treating}} #import // expected-warning{{treating}} +#import // expected-warning{{treating}} @import SomeFramework.public_header2; #endif diff --git a/unittests/Basic/VirtualFileSystemTest.cpp b/unittests/Basic/VirtualFileSystemTest.cpp index 96b8295acdb..ac07035d3e1 100644 --- a/unittests/Basic/VirtualFileSystemTest.cpp +++ b/unittests/Basic/VirtualFileSystemTest.cpp @@ -20,6 +20,18 @@ using namespace llvm; using llvm::sys::fs::UniqueID; namespace { +struct DummyFile : public vfs::File { + vfs::Status S; + explicit DummyFile(vfs::Status S) : S(S) {} + llvm::ErrorOr status() override { return S; } + llvm::ErrorOr> + getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, + bool IsVolatile) override { + llvm_unreachable("unimplemented"); + } + virtual std::error_code close() override { return std::error_code(); } +}; + class DummyFileSystem : public vfs::FileSystem { int FSID; // used to produce UniqueIDs int FileID; // used to produce UniqueIDs @@ -42,7 +54,10 @@ class DummyFileSystem : public vfs::FileSystem { } ErrorOr> openFileForRead(const Twine &Path) override { - llvm_unreachable("unimplemented"); + auto S = status(Path); + if (S) + return std::unique_ptr(new DummyFile{*S}); + return S.getError(); } llvm::ErrorOr getCurrentWorkingDirectory() const override { return std::string(); @@ -718,10 +733,20 @@ TEST_F(VFSFromYAMLTest, MappedFiles) { ErrorOr S = O->status("//root/file1"); ASSERT_FALSE(S.getError()); EXPECT_EQ("//root/foo/bar/a", S->getName()); + EXPECT_TRUE(S->IsVFSMapped); ErrorOr SLower = O->status("//root/foo/bar/a"); EXPECT_EQ("//root/foo/bar/a", SLower->getName()); EXPECT_TRUE(S->equivalent(*SLower)); + EXPECT_FALSE(SLower->IsVFSMapped); + + // file after opening + auto OpenedF = O->openFileForRead("//root/file1"); + ASSERT_FALSE(OpenedF.getError()); + auto OpenedS = (*OpenedF)->status(); + ASSERT_FALSE(OpenedS.getError()); + EXPECT_EQ("//root/foo/bar/a", OpenedS->getName()); + EXPECT_TRUE(OpenedS->IsVFSMapped); // directory S = O->status("//root/"); From f66c5bb67b9a1016b51d2eff0f497d4528dacc0a Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Sat, 12 Dec 2015 12:54:57 -0800 Subject: [PATCH 017/742] Add __has_feature(generalized_swift_name). --- lib/Lex/PPMacroExpansion.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp index a1b99545dd0..e168d0dea8f 100644 --- a/lib/Lex/PPMacroExpansion.cpp +++ b/lib/Lex/PPMacroExpansion.cpp @@ -1085,6 +1085,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .Case("cxx_exceptions", LangOpts.CXXExceptions) .Case("cxx_rtti", LangOpts.RTTI && LangOpts.RTTIData) .Case("enumerator_attributes", true) + .Case("generalized_swift_name", true) .Case("nullability", true) .Case("memory_sanitizer", LangOpts.Sanitize.has(SanitizerKind::Memory)) .Case("thread_sanitizer", LangOpts.Sanitize.has(SanitizerKind::Thread)) From f7fc2cd45ea4fe83f214aa8f6b400096dd0734d4 Mon Sep 17 00:00:00 2001 From: Tim Northover Date: Wed, 27 Jan 2016 19:32:40 +0000 Subject: [PATCH 018/742] ARMv7k: select ABI based on v7k Arch rather than watchos OS. Various bits we'd like to use the new ABI actually compile with "-arch armv7k -miphoneos-version-min=9.0". Not ideal, but also not ridiculous given how slices work. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258976 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Basic/Targets.cpp | 10 +++++----- lib/CodeGen/TargetInfo.cpp | 2 +- lib/Driver/ToolChains.cpp | 4 +++- lib/Driver/Tools.cpp | 3 ++- test/Driver/arch-armv7k.c | 6 ++---- 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/Basic/Targets.cpp b/lib/Basic/Targets.cpp index 4bb77a01ec5..d75aa3e0095 100644 --- a/lib/Basic/Targets.cpp +++ b/lib/Basic/Targets.cpp @@ -4500,7 +4500,7 @@ class ARMTargetInfo : public TargetInfo { Triple.getOS() == llvm::Triple::UnknownOS || StringRef(CPU).startswith("cortex-m")) { setABI("aapcs"); - } else if (Triple.isWatchOS()) { + } else if (Triple.isWatchABI()) { setABI("aapcs16"); } else { setABI("apcs-gnu"); @@ -4716,7 +4716,7 @@ class ARMTargetInfo : public TargetInfo { // Unfortunately, __ARM_ARCH_7K__ is now more of an ABI descriptor. The CPU // happens to be Cortex-A7 though, so it should still get __ARM_ARCH_7A__. - if (getTriple().isWatchOS()) + if (getTriple().isWatchABI()) Builder.defineMacro("__ARM_ARCH_7K__", "2"); if (!CPUAttr.empty()) @@ -4905,8 +4905,8 @@ class ARMTargetInfo : public TargetInfo { BuiltinVaListKind getBuiltinVaListKind() const override { return IsAAPCS ? AAPCSABIBuiltinVaList - : (getTriple().isWatchOS() ? TargetInfo::CharPtrBuiltinVaList - : TargetInfo::VoidPtrBuiltinVaList); + : (getTriple().isWatchABI() ? TargetInfo::CharPtrBuiltinVaList + : TargetInfo::VoidPtrBuiltinVaList); } ArrayRef getGCCRegNames() const override; ArrayRef getGCCRegAliases() const override; @@ -5247,7 +5247,7 @@ class DarwinARMTargetInfo : // ARMleTargetInfo. MaxAtomicInlineWidth = 64; - if (Triple.isWatchOS()) { + if (Triple.isWatchABI()) { // Darwin on iOS uses a variant of the ARM C++ ABI. TheCXXABI.set(TargetCXXABI::WatchOS); diff --git a/lib/CodeGen/TargetInfo.cpp b/lib/CodeGen/TargetInfo.cpp index 2e73ee162ff..d7816260118 100644 --- a/lib/CodeGen/TargetInfo.cpp +++ b/lib/CodeGen/TargetInfo.cpp @@ -4939,7 +4939,7 @@ void ARMABIInfo::computeInfo(CGFunctionInfo &FI) const { /// Return the default calling convention that LLVM will use. llvm::CallingConv::ID ARMABIInfo::getLLVMDefaultCC() const { // The default calling convention that LLVM will infer. - if (isEABIHF() || getTarget().getTriple().isWatchOS()) + if (isEABIHF() || getTarget().getTriple().isWatchABI()) return llvm::CallingConv::ARM_AAPCS_VFP; else if (isEABI()) return llvm::CallingConv::ARM_AAPCS; diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index f57d0871c20..4f72a96ace2 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -1075,7 +1075,9 @@ bool Darwin::UseSjLjExceptions(const ArgList &Args) const { return false; // Only watchOS uses the new DWARF/Compact unwinding method. - return !isTargetWatchOS(); + llvm::Triple Triple(ComputeLLVMTriple(Args)); + return !(Triple.getArchName() == "armv7k" || + Triple.getArchName() == "thumbv7k") && !isTargetWatchOS(); } bool MachO::isPICDefault() const { return true; } diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index e9ff3104f9b..b906513d785 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -698,6 +698,7 @@ arm::FloatABI arm::getARMFloatABI(const ToolChain &TC, const ArgList &Args) { case llvm::Triple::TvOS: { // Darwin defaults to "softfp" for v6 and v7. ABI = (SubArch == 6 || SubArch == 7) ? FloatABI::SoftFP : FloatABI::Soft; + ABI = Triple.isWatchABI() ? FloatABI::Hard : ABI; break; } case llvm::Triple::WatchOS: @@ -954,7 +955,7 @@ void Clang::AddARMTargetArgs(const llvm::Triple &Triple, const ArgList &Args, } else if (Triple.isOSBinFormatMachO()) { if (useAAPCSForMachO(Triple)) { ABIName = "aapcs"; - } else if (Triple.isWatchOS()) { + } else if (Triple.isWatchABI()) { ABIName = "aapcs16"; } else { ABIName = "apcs-gnu"; diff --git a/test/Driver/arch-armv7k.c b/test/Driver/arch-armv7k.c index f5c33cfd9f5..24a1851bc58 100644 --- a/test/Driver/arch-armv7k.c +++ b/test/Driver/arch-armv7k.c @@ -5,9 +5,7 @@ // CHECK-NOT: "-fsjlj-exceptions" // "thumbv7k-apple-ios" is a bit of a weird triple, but since the backend is -// going to choose to use sjlj-based exceptions for it, the front-end needs to +// going to choose to use dwarf-based exceptions for it, the front-end needs to // match. -// RUN: %clang -target x86_64-apple-macosx10.9 -arch armv7k -miphoneos-version-min=9.0 -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SJLJ -// CHECK-SJLJ: "-cc1"{{.*}} "-target-cpu" "cortex-a7" -// CHECK-SJLJ: "-fsjlj-exceptions" +// RUN: %clang -target x86_64-apple-macosx10.9 -arch armv7k -miphoneos-version-min=9.0 -c %s -### 2>&1 | FileCheck %s From 0b9fb102ecaf817e65cfc88987815f9886350504 Mon Sep 17 00:00:00 2001 From: Tim Northover Date: Wed, 27 Jan 2016 22:14:02 +0000 Subject: [PATCH 019/742] ARMv7k: simplify logic for deciding sjlj-exceptions. Slight change of behaviour in the odd armv7+watchos case, which should match the other runtime components. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258994 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Driver/ToolChains.cpp | 3 +-- test/Driver/arch-armv7k.c | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 4f72a96ace2..45cd8bdb29c 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -1076,8 +1076,7 @@ bool Darwin::UseSjLjExceptions(const ArgList &Args) const { // Only watchOS uses the new DWARF/Compact unwinding method. llvm::Triple Triple(ComputeLLVMTriple(Args)); - return !(Triple.getArchName() == "armv7k" || - Triple.getArchName() == "thumbv7k") && !isTargetWatchOS(); + return !Triple.isWatchABI(); } bool MachO::isPICDefault() const { return true; } diff --git a/test/Driver/arch-armv7k.c b/test/Driver/arch-armv7k.c index 24a1851bc58..5ebdd388abd 100644 --- a/test/Driver/arch-armv7k.c +++ b/test/Driver/arch-armv7k.c @@ -9,3 +9,6 @@ // match. // RUN: %clang -target x86_64-apple-macosx10.9 -arch armv7k -miphoneos-version-min=9.0 -c %s -### 2>&1 | FileCheck %s + +// RUN: %clang -target x86_64-apple-macosx10.9 -arch armv7 -mwatchos-version-min=9.0 -c %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-SJLJ +// CHECK-SJLJ: "-fsjlj-exceptions" From fadd7e92047fc42752a2bf658d0d760d175f0fd5 Mon Sep 17 00:00:00 2001 From: Chris Bieneman Date: Tue, 19 Jan 2016 17:06:12 +0000 Subject: [PATCH 020/742] [CMake] Properly respect the CLANG_APPEND_VC_REV option Only set -DSVN_REVISION if CLANG_APPEND_VC_REV=On git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258143 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 30395ea6a760198a47fbc6e9567aafd8970d938c) --- CMakeLists.txt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 54820170291..861fa964f22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -216,14 +216,15 @@ endif() option(CLANG_APPEND_VC_REV "Append the version control system revision id to clang version spew" OFF) +if(CLANG_APPEND_VC_REV) + if(NOT SVN_REVISION) + # This macro will set SVN_REVISION in the parent scope + add_version_info_from_vcs(VERSION_VAR) + endif() -if(NOT SVN_REVISION) - # This macro will set SVN_REVISION in the parent scope - add_version_info_from_vcs(VERSION_VAR) -endif() - -if(SVN_REVISION) - add_definitions(-DSVN_REVISION="${SVN_REVISION}") + if(SVN_REVISION) + add_definitions(-DSVN_REVISION="${SVN_REVISION}") + endif() endif() set(CLANG_VENDOR_UTI "org.llvm.clang" CACHE STRING From 7d90ed028ea4acd0bebb97bee12be547d161c486 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Tue, 19 Jan 2016 18:02:47 +0000 Subject: [PATCH 021/742] Module Debugging: Defer the emission of anonymous tag decls until we are visiting their declcontext. This fixes a regression introduced in r256962: When building debug info for a typdef'd anonymous tag type, we would be visiting the inner anonymous type first thus creating a "typedef changes linkage of anonymous type, but linkage was already computed" error. rdar://problem/24199640 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258152 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit ed2c14216cb447992d4096de7055df10bd80bc4c) --- lib/CodeGen/ObjectFilePCHContainerOperations.cpp | 4 ++++ test/Modules/ExtDebugInfo.cpp | 11 +++++++++++ test/Modules/Inputs/DebugCXX.h | 4 ++++ test/Modules/ModuleDebugInfo.cpp | 12 ++++++++++++ 4 files changed, 31 insertions(+) diff --git a/lib/CodeGen/ObjectFilePCHContainerOperations.cpp b/lib/CodeGen/ObjectFilePCHContainerOperations.cpp index f385e53fa01..2c0c3ca8a90 100644 --- a/lib/CodeGen/ObjectFilePCHContainerOperations.cpp +++ b/lib/CodeGen/ObjectFilePCHContainerOperations.cpp @@ -190,6 +190,10 @@ class PCHContainerGenerator : public ASTConsumer { if (D->isFromASTFile()) return; + // Anonymous tag decls are deferred until we are building their declcontext. + if (D->getName().empty()) + return; + DebugTypeVisitor DTV(*Builder->getModuleDebugInfo(), *Ctx, false); DTV.TraverseDecl(D); Builder->UpdateCompletedType(D); diff --git a/test/Modules/ExtDebugInfo.cpp b/test/Modules/ExtDebugInfo.cpp index b255042cbfe..b0bca88b89e 100644 --- a/test/Modules/ExtDebugInfo.cpp +++ b/test/Modules/ExtDebugInfo.cpp @@ -35,6 +35,10 @@ enum { auto anon_enum = DebugCXX::e2; char _anchor = anon_enum + conflicting_uid; +TypedefUnion tdu; +TypedefEnum tde; +TypedefStruct tds; + // CHECK: ![[NS:.*]] = !DINamespace(name: "DebugCXX", scope: ![[MOD:[0-9]+]], // CHECK: ![[MOD]] = !DIModule(scope: null, name: {{.*}}DebugCXX @@ -62,6 +66,13 @@ char _anchor = anon_enum + conflicting_uid; // CHECK-SAME: flags: DIFlagFwdDecl, // CHECK-SAME: identifier: "_ZTSN8DebugCXX8TemplateIfNS_6traitsIfEEEE") +// CHECK: !DICompositeType(tag: DW_TAG_union_type, +// CHECK-SAME: flags: DIFlagFwdDecl, identifier: "_ZTS12TypedefUnion") +// CHECK: !DICompositeType(tag: DW_TAG_enumeration_type, +// CHECK-SAME: flags: DIFlagFwdDecl, identifier: "_ZTS11TypedefEnum") +// CHECK: !DICompositeType(tag: DW_TAG_structure_type, +// CHECK-SAME: flags: DIFlagFwdDecl, identifier: "_ZTS13TypedefStruct") + // CHECK: !DIDerivedType(tag: DW_TAG_member, name: "static_member", // CHECK-SAME: scope: !"_ZTSN8DebugCXX6StructE" diff --git a/test/Modules/Inputs/DebugCXX.h b/test/Modules/Inputs/DebugCXX.h index b6a52579fc3..285e094171c 100644 --- a/test/Modules/Inputs/DebugCXX.h +++ b/test/Modules/Inputs/DebugCXX.h @@ -58,3 +58,7 @@ class FwdVirtual { }; struct PureForwardDecl; + +typedef union { int i; } TypedefUnion; +typedef enum { e0 = 0 } TypedefEnum; +typedef struct { int i; } TypedefStruct; diff --git a/test/Modules/ModuleDebugInfo.cpp b/test/Modules/ModuleDebugInfo.cpp index bbe36cb225d..c1248fd4d8e 100644 --- a/test/Modules/ModuleDebugInfo.cpp +++ b/test/Modules/ModuleDebugInfo.cpp @@ -28,6 +28,10 @@ // CHECK-SAME: identifier: "_ZTSN8DebugCXX4EnumE") // CHECK: !DINamespace(name: "DebugCXX" +// CHECK: !DICompositeType(tag: DW_TAG_enumeration_type, +// CHECK-SAME-NOT: name: +// CHECK-SAME: identifier: "_ZTS11TypedefEnum") + // CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "Struct" // CHECK-SAME: identifier: "_ZTSN8DebugCXX6StructE") @@ -47,6 +51,14 @@ // CHECK-SAME: identifier: "_ZTS10FwdVirtual") // CHECK: !DIDerivedType(tag: DW_TAG_member, name: "_vptr$FwdVirtual" +// CHECK: !DICompositeType(tag: DW_TAG_union_type, +// CHECK-SAME-NOT: name: +// CHECK-SAME: identifier: "_ZTS12TypedefUnion") + +// CHECK: !DICompositeType(tag: DW_TAG_structure_type, +// CHECK-SAME-NOT: name: +// CHECK-SAME: identifier: "_ZTS13TypedefStruct") + // CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "FloatInstatiation" // no mangled name here yet. From 087ecae9e41a58b5077ecd16dd04d6cf368c5492 Mon Sep 17 00:00:00 2001 From: Chris Bieneman Date: Tue, 19 Jan 2016 22:41:51 +0000 Subject: [PATCH 022/742] [CMake] Creating add_clang_tool macro to wrap add_clang_executable and generate install actions and targets. This change brings forward the LLVM convention that "executables" are just runnable binaries, and "tools" are executables that are part of the project's install. Having this abstraction will allow us to simplify some of the tool CMakeLists files, and it will standardize some of the install behaviors. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258209 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 2f1b3a21152c2a7db3c8949b8a8247510eb46948) --- CMakeLists.txt | 16 ++++++++++++++++ tools/driver/CMakeLists.txt | 14 +------------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 861fa964f22..272c80eaf39 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -444,6 +444,22 @@ macro(add_clang_executable name) set_clang_windows_version_resource_properties(${name}) endmacro(add_clang_executable) +macro(add_clang_tool name) + add_clang_executable(${name} ${ARGN}) + install(TARGETS ${name} + EXPORT ClangTargets + RUNTIME DESTINATION bin + COMPONENT ${name}) + + if(NOT CMAKE_CONFIGURATION_TYPES) + add_custom_target(install-${name} + DEPENDS ${name} + COMMAND "${CMAKE_COMMAND}" + -DCMAKE_INSTALL_COMPONENT=${name} + -P "${CMAKE_BINARY_DIR}/cmake_install.cmake") + endif() +endmacro() + macro(add_clang_symlink name dest) add_llvm_tool_symlink(${name} ${dest} ALWAYS_GENERATE) # Always generate install targets diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt index 34d3e434d3e..73ad4fdce21 100644 --- a/tools/driver/CMakeLists.txt +++ b/tools/driver/CMakeLists.txt @@ -24,7 +24,7 @@ if(CLANG_PLUGIN_SUPPORT) set(LLVM_NO_DEAD_STRIP 1) endif() -add_clang_executable(clang +add_clang_tool(clang driver.cpp cc1_main.cpp cc1as_main.cpp @@ -53,18 +53,6 @@ endif() add_dependencies(clang clang-headers) -install(TARGETS clang - RUNTIME DESTINATION bin - COMPONENT clang) - -if(NOT CMAKE_CONFIGURATION_TYPES) - add_custom_target(install-clang - DEPENDS clang - COMMAND "${CMAKE_COMMAND}" - -DCMAKE_INSTALL_COMPONENT=clang - -P "${CMAKE_BINARY_DIR}/cmake_install.cmake") -endif() - if(NOT CLANG_LINKS_TO_CREATE) set(CLANG_LINKS_TO_CREATE clang++ clang-cl) From 92304acd0d92a112b50b283b823fc8bb7d66c612 Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Tue, 19 Jan 2016 22:54:26 +0000 Subject: [PATCH 023/742] Allow __attribute__((mode)) to appertain to field declarations again. Corrects compile issues with LibreOffice. Patch by Stephan Bergmann git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258213 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 1d2a84e04f6c7953e9c6a0b9680e1235410e5d1e) --- include/clang/Basic/Attr.td | 4 ++-- include/clang/Basic/DiagnosticSemaKinds.td | 3 ++- include/clang/Sema/AttributeList.h | 3 ++- lib/Sema/SemaDeclAttr.cpp | 4 ++-- test/Sema/attr-mode.c | 8 ++++++-- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index e467c8ce7ac..a7e073d5cae 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -883,8 +883,8 @@ def MipsInterrupt : InheritableAttr, TargetSpecificAttr { def Mode : Attr { let Spellings = [GCC<"mode">]; - let Subjects = SubjectList<[Var, TypedefName], ErrorDiag, - "ExpectedVariableOrTypedef">; + let Subjects = SubjectList<[Var, TypedefName, Field], ErrorDiag, + "ExpectedVariableFieldOrTypedef">; let Args = [IdentifierArgument<"Mode">]; let Documentation = [Undocumented]; } diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index e62087de667..5d235b1b888 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -2443,7 +2443,8 @@ def warn_attribute_wrong_decl_type : Warning< "Objective-C instance methods|init methods of interface or class extension declarations|" "variables, functions and classes|Objective-C protocols|" "functions and global variables|structs, unions, and typedefs|structs and typedefs|" - "interface or protocol declarations|kernel functions|non-K&R-style functions}1">, + "interface or protocol declarations|kernel functions|non-K&R-style functions|" + "variables, fields and typedefs}1">, InGroup; def err_attribute_wrong_decl_type : Error; def warn_type_attribute_wrong_type : Warning< diff --git a/include/clang/Sema/AttributeList.h b/include/clang/Sema/AttributeList.h index e32781d35fc..1a53a2c3d4a 100644 --- a/include/clang/Sema/AttributeList.h +++ b/include/clang/Sema/AttributeList.h @@ -855,7 +855,8 @@ enum AttributeDeclKind { ExpectedStructOrTypedef, ExpectedObjectiveCInterfaceOrProtocol, ExpectedKernelFunction, - ExpectedFunctionWithProtoType + ExpectedFunctionWithProtoType, + ExpectedVariableFieldOrTypedef }; } // end namespace clang diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 92a7161e75e..c1c4d1e75a5 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -3420,7 +3420,7 @@ static void handleModeAttr(Sema &S, Decl *D, const AttributeList &Attr) { if (TypedefNameDecl *TD = dyn_cast(D)) OldTy = TD->getUnderlyingType(); else - OldTy = cast(D)->getType(); + OldTy = cast(D)->getType(); // Base type can also be a vector type (see PR17453). // Distinguish between base type and base element type. @@ -3493,7 +3493,7 @@ static void handleModeAttr(Sema &S, Decl *D, const AttributeList &Attr) { if (TypedefNameDecl *TD = dyn_cast(D)) TD->setModedTypeSourceInfo(TD->getTypeSourceInfo(), NewTy); else - cast(D)->setType(NewTy); + cast(D)->setType(NewTy); D->addAttr(::new (S.Context) ModeAttr(Attr.getRange(), S.Context, Name, diff --git a/test/Sema/attr-mode.c b/test/Sema/attr-mode.c index 77bd2f737a4..179b1819b1f 100644 --- a/test/Sema/attr-mode.c +++ b/test/Sema/attr-mode.c @@ -24,8 +24,8 @@ typedef unsigned unwind_word __attribute((mode(unwind_word))); int **__attribute((mode(QI)))* i32; // expected-error{{mode attribute}} -__attribute__((mode(QI))) int invalid_func() { return 1; } // expected-error{{'mode' attribute only applies to variables and typedefs}} -enum invalid_enum { A1 __attribute__((mode(QI))) }; // expected-error{{'mode' attribute only applies to variables and typedefs}} +__attribute__((mode(QI))) int invalid_func() { return 1; } // expected-error{{'mode' attribute only applies to variables, fields and typedefs}} +enum invalid_enum { A1 __attribute__((mode(QI))) }; // expected-error{{'mode' attribute only applies to variables, fields and typedefs}} typedef _Complex double c32 __attribute((mode(SC))); int c32_test[sizeof(c32) == 8 ? 1 : -1]; @@ -76,3 +76,7 @@ void test_TCtype(c128ibm *a) { f_ft128_complex_arg (a); } #else #error Unknown test architecture. #endif + +struct S { + int n __attribute((mode(HI))); +}; From 9332596e61ef47770a238f430a415cc51ea24cbd Mon Sep 17 00:00:00 2001 From: Chris Bieneman Date: Tue, 19 Jan 2016 23:01:38 +0000 Subject: [PATCH 024/742] [CMake] Don't apply Export set to clang tools I can't apply export to tools without getting some strange CMake spew. The behavior here is a bit unexpected. CMake is complaining about static link dependencies not being in the same export set, which shouldn't matter. In the short term it is easier to just remove the export set (which was just added in r258209) while I sort this out. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258214 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 82991f2a04c2f403d2473bd6c6bb659cf15ab7f5) --- CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 272c80eaf39..ca3b51fddd4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -447,7 +447,6 @@ endmacro(add_clang_executable) macro(add_clang_tool name) add_clang_executable(${name} ${ARGN}) install(TARGETS ${name} - EXPORT ClangTargets RUNTIME DESTINATION bin COMPONENT ${name}) From 06e8056e472f5f67af61cae750eadaa791b85ae9 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Tue, 19 Jan 2016 23:42:44 +0000 Subject: [PATCH 025/742] Module Debugging: Make sure that anonymous tag decls that define global variables are visited. This shouldn't encourage anyone to put global variables into clang modules. rdar://problem/24199640 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258250 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 135b6cd218276eaf2c9564b93e28e9da830f5928) --- lib/CodeGen/ObjectFilePCHContainerOperations.cpp | 13 ++++++------- test/Modules/ExtDebugInfo.cpp | 4 ++++ test/Modules/Inputs/DebugCXX.h | 4 ++++ test/Modules/ModuleDebugInfo.cpp | 16 ++++++++++++++++ 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/lib/CodeGen/ObjectFilePCHContainerOperations.cpp b/lib/CodeGen/ObjectFilePCHContainerOperations.cpp index 2c0c3ca8a90..cd17832554d 100644 --- a/lib/CodeGen/ObjectFilePCHContainerOperations.cpp +++ b/lib/CodeGen/ObjectFilePCHContainerOperations.cpp @@ -59,10 +59,8 @@ class PCHContainerGenerator : public ASTConsumer { struct DebugTypeVisitor : public RecursiveASTVisitor { clang::CodeGen::CGDebugInfo &DI; ASTContext &Ctx; - bool SkipTagDecls; - DebugTypeVisitor(clang::CodeGen::CGDebugInfo &DI, ASTContext &Ctx, - bool SkipTagDecls) - : DI(DI), Ctx(Ctx), SkipTagDecls(SkipTagDecls) {} + DebugTypeVisitor(clang::CodeGen::CGDebugInfo &DI, ASTContext &Ctx) + : DI(DI), Ctx(Ctx) {} /// Determine whether this type can be represented in DWARF. static bool CanRepresent(const Type *Ty) { @@ -80,7 +78,8 @@ class PCHContainerGenerator : public ASTConsumer { // TagDecls may be deferred until after all decls have been merged and we // know the complete type. Pure forward declarations will be skipped, but // they don't need to be emitted into the module anyway. - if (SkipTagDecls && isa(D)) + if (auto *TD = dyn_cast(D)) + if (!TD->isCompleteDefinition()) return true; QualType QualTy = Ctx.getTypeDeclType(D); @@ -173,7 +172,7 @@ class PCHContainerGenerator : public ASTConsumer { // Collect debug info for all decls in this group. for (auto *I : D) if (!I->isFromASTFile()) { - DebugTypeVisitor DTV(*Builder->getModuleDebugInfo(), *Ctx, true); + DebugTypeVisitor DTV(*Builder->getModuleDebugInfo(), *Ctx); DTV.TraverseDecl(I); } return true; @@ -194,7 +193,7 @@ class PCHContainerGenerator : public ASTConsumer { if (D->getName().empty()) return; - DebugTypeVisitor DTV(*Builder->getModuleDebugInfo(), *Ctx, false); + DebugTypeVisitor DTV(*Builder->getModuleDebugInfo(), *Ctx); DTV.TraverseDecl(D); Builder->UpdateCompletedType(D); } diff --git a/test/Modules/ExtDebugInfo.cpp b/test/Modules/ExtDebugInfo.cpp index b0bca88b89e..6c5bd559fc9 100644 --- a/test/Modules/ExtDebugInfo.cpp +++ b/test/Modules/ExtDebugInfo.cpp @@ -39,6 +39,10 @@ TypedefUnion tdu; TypedefEnum tde; TypedefStruct tds; +void foo() { + GlobalStruct.i = GlobalUnion.i = GlobalEnum; +} + // CHECK: ![[NS:.*]] = !DINamespace(name: "DebugCXX", scope: ![[MOD:[0-9]+]], // CHECK: ![[MOD]] = !DIModule(scope: null, name: {{.*}}DebugCXX diff --git a/test/Modules/Inputs/DebugCXX.h b/test/Modules/Inputs/DebugCXX.h index 285e094171c..cd2b7a67498 100644 --- a/test/Modules/Inputs/DebugCXX.h +++ b/test/Modules/Inputs/DebugCXX.h @@ -62,3 +62,7 @@ struct PureForwardDecl; typedef union { int i; } TypedefUnion; typedef enum { e0 = 0 } TypedefEnum; typedef struct { int i; } TypedefStruct; + +union { int i; } GlobalUnion; +struct { int i; } GlobalStruct; +enum { e5 = 5 } GlobalEnum; diff --git a/test/Modules/ModuleDebugInfo.cpp b/test/Modules/ModuleDebugInfo.cpp index c1248fd4d8e..1f574568b6f 100644 --- a/test/Modules/ModuleDebugInfo.cpp +++ b/test/Modules/ModuleDebugInfo.cpp @@ -28,10 +28,20 @@ // CHECK-SAME: identifier: "_ZTSN8DebugCXX4EnumE") // CHECK: !DINamespace(name: "DebugCXX" +// CHECK: !DICompositeType(tag: DW_TAG_enumeration_type, +// CHECK-SAME-NOT: name: + +// CHECK: !DICompositeType(tag: DW_TAG_enumeration_type, +// CHECK-SAME-NOT: name: + // CHECK: !DICompositeType(tag: DW_TAG_enumeration_type, // CHECK-SAME-NOT: name: // CHECK-SAME: identifier: "_ZTS11TypedefEnum") +// CHECK: !DICompositeType(tag: DW_TAG_enumeration_type, +// CHECK-SAME-NOT: name: +// CHECK: !DIEnumerator(name: "e5", value: 5) + // CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "Struct" // CHECK-SAME: identifier: "_ZTSN8DebugCXX6StructE") @@ -65,4 +75,10 @@ // CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "B", // no mangled name here yet. +// CHECK: !DICompositeType(tag: DW_TAG_union_type, +// CHECK-SAME-NOT: name: + +// CHECK: !DICompositeType(tag: DW_TAG_structure_type, +// CHECK-SAME-NOT: name: + // CHECK-NEG-NOT: !DICompositeType(tag: DW_TAG_structure_type, name: "PureForwardDecl" From 6c52e129fb6f2dbf3e2086961ec30128ea3696c1 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Tue, 19 Jan 2016 23:42:53 +0000 Subject: [PATCH 026/742] Module Debugging: Don't emit external type references to anonymous types. Even if they exist in the module, they can't be matched with the forward declaration in the object file. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258251 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 4732653928fbe889856d02682478d306e6759c57) --- lib/CodeGen/CGDebugInfo.cpp | 5 +++-- test/Modules/ExtDebugInfo.cpp | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index 2511b249f0f..fa501fce756 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -1536,8 +1536,9 @@ static bool shouldOmitDefinition(CodeGenOptions::DebugInfoKind DebugKind, const RecordDecl *RD, const LangOptions &LangOpts) { // Does the type exist in an imported clang module? - if (DebugTypeExtRefs && RD->isFromASTFile() && RD->getDefinition()) - return true; + if (DebugTypeExtRefs && RD->isFromASTFile() && RD->getDefinition() && + RD->isExternallyVisible()) + return true; if (DebugKind > CodeGenOptions::LimitedDebugInfo) return false; diff --git a/test/Modules/ExtDebugInfo.cpp b/test/Modules/ExtDebugInfo.cpp index 6c5bd559fc9..101b0f1e16b 100644 --- a/test/Modules/ExtDebugInfo.cpp +++ b/test/Modules/ExtDebugInfo.cpp @@ -84,4 +84,13 @@ void foo() { // CHECK: !DICompositeType(tag: DW_TAG_enumeration_type, scope: ![[NS]], // CHECK-SAME: line: 16 +// CHECK: !DIGlobalVariable(name: "GlobalUnion", +// CHECK-SAME: type: ![[GLOBAL_UNION:[0-9]+]] +// CHECK: ![[GLOBAL_UNION]] = !DICompositeType(tag: DW_TAG_union_type, +// CHECK-SAME: elements: !{{[0-9]+}}) +// CHECK: !DIGlobalVariable(name: "GlobalStruct", +// CHECK-SAME: type: ![[GLOBAL_STRUCT:[0-9]+]] +// CHECK: ![[GLOBAL_STRUCT]] = !DICompositeType(tag: DW_TAG_structure_type, +// CHECK-SAME: elements: !{{[0-9]+}}) + // CHECK: !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !0, entity: !"_ZTSN8DebugCXX6StructE", line: 24) From 5c31ba012a388eb29a70ceaff218dc8bc3e89570 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Tue, 19 Jan 2016 23:42:56 +0000 Subject: [PATCH 027/742] Module Debugging: Add Objective-C testcases for anonymous tag decls. (NFC) rdar://problem/24199640 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258252 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 5c10539da2d97383808d389bbf6ee2851568f014) --- test/Modules/ExtDebugInfo.m | 28 ++++++++++++++++++++++++++++ test/Modules/Inputs/DebugObjC.h | 14 ++++++++++++++ test/Modules/ModuleDebugInfo.m | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+) diff --git a/test/Modules/ExtDebugInfo.m b/test/Modules/ExtDebugInfo.m index 8e063f09048..416f2813cec 100644 --- a/test/Modules/ExtDebugInfo.m +++ b/test/Modules/ExtDebugInfo.m @@ -18,8 +18,13 @@ @import DebugObjC; #endif +TypedefUnion tdu; +TypedefEnum tde; +TypedefStruct tds; + int foo(ObjCClass *c) { InnerEnum e = e0; + GlobalStruct.i = GlobalUnion.i = GlobalEnum; [c instanceMethodWithInt: 0]; return [c property]; } @@ -30,6 +35,29 @@ int foo(ObjCClass *c) { // CHECK-SAME: flags: DIFlagFwdDecl) // CHECK-NOT: !DICompositeType(tag: DW_TAG_structure_type, // CHECK: ![[MOD]] = !DIModule(scope: null, name: {{.*}}DebugObjC + +// CHECK: !DIGlobalVariable(name: "GlobalUnion", +// CHECK-SAME: type: ![[GLOBAL_UNION:[0-9]+]] +// CHECK: ![[GLOBAL_UNION]] = !DICompositeType(tag: DW_TAG_union_type, +// CHECK-SAME: elements: !{{[0-9]+}}) +// CHECK: !DIGlobalVariable(name: "GlobalStruct", +// CHECK-SAME: type: ![[GLOBAL_STRUCT:[0-9]+]] +// CHECK: ![[GLOBAL_STRUCT]] = !DICompositeType(tag: DW_TAG_structure_type, +// CHECK-SAME: elements: !{{[0-9]+}}) + +// CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "TypedefUnion", +// CHECK-SAME: baseType: ![[TD_UNION:.*]]) +// CHECK: ![[TD_UNION]] = !DICompositeType(tag: DW_TAG_union_type, +// CHECK-SAME: flags: DIFlagFwdDecl) +// CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "TypedefEnum", +// CHECK-SAME: baseType: ![[TD_ENUM:.*]]) +// CHECK: ![[TD_ENUM]] = !DICompositeType(tag: DW_TAG_enumeration_type, +// CHECK-SAME: flags: DIFlagFwdDecl) +// CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "TypedefStruct", +// CHECK-SAME: baseType: ![[TD_STRUCT:.*]]) +// CHECK: ![[TD_STRUCT]] = !DICompositeType(tag: DW_TAG_structure_type, +// CHECK-SAME: flags: DIFlagFwdDecl) + // CHECK-NOT: !DICompositeType(tag: DW_TAG_structure_type, // CHECK: !DICompositeType(tag: DW_TAG_enumeration_type, // CHECK-SAME: scope: ![[MOD]], diff --git a/test/Modules/Inputs/DebugObjC.h b/test/Modules/Inputs/DebugObjC.h index bde463abfd6..eb7a9f9da9f 100644 --- a/test/Modules/Inputs/DebugObjC.h +++ b/test/Modules/Inputs/DebugObjC.h @@ -22,3 +22,17 @@ typedef enum { + (InnerEnum)protocolMethod; @end + +struct FwdDeclared; +struct FwdDeclared { + int i; +}; +struct PureForwardDecl; + +typedef union { int i; } TypedefUnion; +typedef enum { e1 = 1 } TypedefEnum; +typedef struct { int i; } TypedefStruct; + +union { int i; } GlobalUnion; +struct { int i; } GlobalStruct; +enum { e2 = 2 } GlobalEnum; diff --git a/test/Modules/ModuleDebugInfo.m b/test/Modules/ModuleDebugInfo.m index 0974f38cc22..cba4063b1de 100644 --- a/test/Modules/ModuleDebugInfo.m +++ b/test/Modules/ModuleDebugInfo.m @@ -38,14 +38,47 @@ // MODULE-CHECK: !DICompositeType(tag: DW_TAG_enumeration_type, // MODULE-CHECK-SAME: scope: ![[MODULE:[0-9]+]], // MODULE-CHECK: ![[MODULE]] = !DIModule(scope: null, name: "DebugObjC" + +// MODULE-CHECK: ![[TD_ENUM:.*]] = !DICompositeType(tag: DW_TAG_enumeration_type, +// MODULE-CHECK-SAME-NOT: name: +// MODULE-CHECK-SAME: elements: + // MODULE-CHECK: !DICompositeType(tag: DW_TAG_structure_type, // MODULE-CHECK-SAME: name: "FwdDecl", // MODULE-CHECK-SAME: scope: ![[MODULE]], // MODULE-CHECK: !DICompositeType(tag: DW_TAG_structure_type, // MODULE-CHECK-SAME: name: "ObjCClass", // MODULE-CHECK-SAME: scope: ![[MODULE]], + +// MODULE-CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "FwdDeclared" +// MODULE-CHECK-SAME: elements: + +// MODULE-CHECK: ![[TD_UNION:.*]] = !DICompositeType(tag: DW_TAG_union_type, +// MODULE-CHECK-SAME-NOT: name: +// MODULE-CHECK-SAME: elements: + +// MODULE-CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "TypedefUnion", +// MODULE-CHECK-SAME: baseType: ![[TD_UNION]]) + +// MODULE-CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "TypedefEnum", +// MODULE-CHECK-SAME: baseType: ![[TD_ENUM:.*]]) + +// MODULE-CHECK: ![[TD_STRUCT:.*]] = !DICompositeType(tag: DW_TAG_structure_type, +// MODULE-CHECK-SAME-NOT: name: +// MODULE-CHECK-SAME: elements: +// MODULE-CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "TypedefStruct", +// MODULE-CHECK-SAME: baseType: ![[TD_STRUCT]]) + +// MODULE-CHECK: !DICompositeType(tag: DW_TAG_union_type, +// MODULE-CHECK-SAME-NOT: name: + +// MODULE-CHECK: !DICompositeType(tag: DW_TAG_structure_type, +// MODULE-CHECK-SAME-NOT: name: + // MODULE-CHECK: !DISubprogram(name: "+[ObjCClass classMethod]", // MODULE-CHECK-SAME: scope: ![[MODULE]], // The forward declaration should not be in the module scope. // MODULE-CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "OpaqueData", file + +// MODULE-CHECK-NEG-NOT: !DICompositeType(tag: DW_TAG_structure_type, name: "PureForwardDecl" From 95f10eba8c32fc255634c224909cea627b907aac Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Wed, 20 Jan 2016 01:29:34 +0000 Subject: [PATCH 028/742] Module Debugging: Fine-tune the condition that determines whether a type can be found in a module. There are externally visible anonymous types that can be found: typedef struct { } s; // I can be found via the typedef. There are anonymous internal types that can be found: namespace { struct s {}; } // I can be found by name. rdar://problem/24199640 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258272 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 3d5d4c39659f11dfbe8e11c857cadf5c449b559b) --- lib/CodeGen/CGDebugInfo.cpp | 2 +- test/Modules/ExtDebugInfo.cpp | 10 +++++++++- test/Modules/Inputs/DebugCXX.h | 6 ++++++ test/Modules/ModuleDebugInfo.cpp | 4 ++++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index fa501fce756..28e28489681 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -1537,7 +1537,7 @@ static bool shouldOmitDefinition(CodeGenOptions::DebugInfoKind DebugKind, const LangOptions &LangOpts) { // Does the type exist in an imported clang module? if (DebugTypeExtRefs && RD->isFromASTFile() && RD->getDefinition() && - RD->isExternallyVisible()) + (RD->isExternallyVisible() || !RD->getName().empty())) return true; if (DebugKind > CodeGenOptions::LimitedDebugInfo) diff --git a/test/Modules/ExtDebugInfo.cpp b/test/Modules/ExtDebugInfo.cpp index 101b0f1e16b..b9eae12deda 100644 --- a/test/Modules/ExtDebugInfo.cpp +++ b/test/Modules/ExtDebugInfo.cpp @@ -39,8 +39,10 @@ TypedefUnion tdu; TypedefEnum tde; TypedefStruct tds; +InAnonymousNamespace anon; + void foo() { - GlobalStruct.i = GlobalUnion.i = GlobalEnum; + anon.i = GlobalStruct.i = GlobalUnion.i = GlobalEnum; } // CHECK: ![[NS:.*]] = !DINamespace(name: "DebugCXX", scope: ![[MOD:[0-9]+]], @@ -93,4 +95,10 @@ void foo() { // CHECK: ![[GLOBAL_STRUCT]] = !DICompositeType(tag: DW_TAG_structure_type, // CHECK-SAME: elements: !{{[0-9]+}}) +// CHECK: !DIGlobalVariable(name: "anon", +// CHECK-SAME: type: ![[GLOBAL_ANON:[0-9]+]] +// CHECK: ![[GLOBAL_ANON]] = !DICompositeType(tag: DW_TAG_structure_type, +// CHECK-SAME: name: "InAnonymousNamespace", {{.*}}DIFlagFwdDecl) + + // CHECK: !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !0, entity: !"_ZTSN8DebugCXX6StructE", line: 24) diff --git a/test/Modules/Inputs/DebugCXX.h b/test/Modules/Inputs/DebugCXX.h index cd2b7a67498..39dda959ce2 100644 --- a/test/Modules/Inputs/DebugCXX.h +++ b/test/Modules/Inputs/DebugCXX.h @@ -66,3 +66,9 @@ typedef struct { int i; } TypedefStruct; union { int i; } GlobalUnion; struct { int i; } GlobalStruct; enum { e5 = 5 } GlobalEnum; + +namespace { + namespace { + struct InAnonymousNamespace { int i; }; + } +} diff --git a/test/Modules/ModuleDebugInfo.cpp b/test/Modules/ModuleDebugInfo.cpp index 1f574568b6f..a2b735b3b70 100644 --- a/test/Modules/ModuleDebugInfo.cpp +++ b/test/Modules/ModuleDebugInfo.cpp @@ -81,4 +81,8 @@ // CHECK: !DICompositeType(tag: DW_TAG_structure_type, // CHECK-SAME-NOT: name: +// CHECK: !DICompositeType(tag: DW_TAG_structure_type, +// CHECK-SAME: name: "InAnonymousNamespace", +// CHECK-SAME: elements: !{{[0-9]+}}) + // CHECK-NEG-NOT: !DICompositeType(tag: DW_TAG_structure_type, name: "PureForwardDecl" From 7dae19826304c869cdef1ef3f38e265e07b2af35 Mon Sep 17 00:00:00 2001 From: Alexey Bataev Date: Wed, 20 Jan 2016 05:25:51 +0000 Subject: [PATCH 029/742] Fix infinite loop when ::new or ::delete are found in member initializer list, by Denis Zobnin Fix for an infinite loop on parsing ::new or ::delete in member initializer list, found by fuzzing PR23057, comment #33. Skip the rest of the member initializers if the previous initializer was invalid. Differential Revision: http://reviews.llvm.org/D16216 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258290 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 7506929a38783ed7038f893c8f1f59f6a4ccfd20) --- lib/Parse/ParseDeclCXX.cpp | 26 +++++++------- test/Parser/cxx-invalid-function-decl.cpp | 42 +++++++++++++++++++++++ 2 files changed, 56 insertions(+), 12 deletions(-) create mode 100644 test/Parser/cxx-invalid-function-decl.cpp diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index f11fcb8d768..b2337b2f001 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -3189,28 +3189,30 @@ void Parser::ParseConstructorInitializer(Decl *ConstructorDecl) { Actions.CodeCompleteConstructorInitializer(ConstructorDecl, MemInitializers); return cutOffParsing(); - } else { - MemInitResult MemInit = ParseMemInitializer(ConstructorDecl); - if (!MemInit.isInvalid()) - MemInitializers.push_back(MemInit.get()); - else - AnyErrors = true; } - + + MemInitResult MemInit = ParseMemInitializer(ConstructorDecl); + if (!MemInit.isInvalid()) + MemInitializers.push_back(MemInit.get()); + else + AnyErrors = true; + if (Tok.is(tok::comma)) ConsumeToken(); else if (Tok.is(tok::l_brace)) break; - // If the next token looks like a base or member initializer, assume that - // we're just missing a comma. - else if (Tok.isOneOf(tok::identifier, tok::coloncolon)) { + // If the previous initializer was valid and the next token looks like a + // base or member initializer, assume that we're just missing a comma. + else if (!MemInit.isInvalid() && + Tok.isOneOf(tok::identifier, tok::coloncolon)) { SourceLocation Loc = PP.getLocForEndOfToken(PrevTokLocation); Diag(Loc, diag::err_ctor_init_missing_comma) << FixItHint::CreateInsertion(Loc, ", "); } else { // Skip over garbage, until we get to '{'. Don't eat the '{'. - Diag(Tok.getLocation(), diag::err_expected_either) << tok::l_brace - << tok::comma; + if (!MemInit.isInvalid()) + Diag(Tok.getLocation(), diag::err_expected_either) << tok::l_brace + << tok::comma; SkipUntil(tok::l_brace, StopAtSemi | StopBeforeMatch); break; } diff --git a/test/Parser/cxx-invalid-function-decl.cpp b/test/Parser/cxx-invalid-function-decl.cpp new file mode 100644 index 00000000000..2db27516eef --- /dev/null +++ b/test/Parser/cxx-invalid-function-decl.cpp @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +// Check that "::new" and "::delete" in member initializer list are diagnosed +// correctly and don't lead to infinite loop on parsing. + +// Error: X() (initializer on non-constructor), "::new" is skipped. +void f1() : X() ::new{}; // expected-error{{only constructors take base initializers}} + +// Errors: first "::delete" and initializer on non-constructor, others skipped. +void f2() : ::delete, ::new, X() ::new ::delete{} // expected-error{{expected class member or base class name}} + // expected-error@-1{{only constructors take base initializers}} + +// Errors: the '::' token, "::delete" and initializer on non-constructor, others skipped. +void f3() : ::, ::delete X(), ::new {}; // expected-error2{{expected class member or base class name}} + // expected-error@-1{{only constructors take base initializers}} + +template +struct Base1 { + T x1; + Base1(T a1) : x1(a1) {} +}; + +template +struct Base2 { + T x2; + Base2(T a2) : x2(a2) {} +}; + +struct S : public Base1, public Base2 { + int x; + + // 1-st initializer is correct (just missing ','), 2-nd incorrect, skip other. + S() : ::Base1(0) ::new, ::Base2(1.0) ::delete x(2) {} // expected-error{{expected class member or base class name}} + // expected-error@-1{{missing ',' between base or member initializers}} + + // 1-st and 2-nd are correct, errors: '::' and "::new", others skipped. + S(int a) : Base1(a), ::Base2(1.0), ::, // expected-error{{expected class member or base class name}} + ::new, ! ::delete, ::Base2<() x(3) {} // expected-error{{expected class member or base class name}} + + // All initializers are correct, nothing to skip, diagnose 2 missing commas. + S(const S &) : Base1(0) ::Base2(1.0) x(2) {} // expected-error2{{missing ',' between base or member initializers}} +}; From bb2189f2e7a6bf965e82b2d05db99c90cb61d8e6 Mon Sep 17 00:00:00 2001 From: Alexey Bataev Date: Thu, 21 Jan 2016 12:54:48 +0000 Subject: [PATCH 030/742] Fix crash for typedefs for arrays of runtime bounds in Lambdas/Captured Statements, used in sizeof() expression only. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258396 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 8a851046bed598fd4cf3251028781191324c900c) --- lib/Sema/SemaExpr.cpp | 251 ++++++++++++++----------- test/CodeGenCXX/lambda-expressions.cpp | 53 ++++-- 2 files changed, 170 insertions(+), 134 deletions(-) diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index f8537d3692b..5ba95a2fbda 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -3748,6 +3748,128 @@ bool Sema::CheckVecStepExpr(Expr *E) { return CheckUnaryExprOrTypeTraitOperand(E, UETT_VecStep); } +static void captureVariablyModifiedType(ASTContext &Context, QualType T, + CapturingScopeInfo *CSI) { + assert(T->isVariablyModifiedType()); + assert(CSI != nullptr); + + // We're going to walk down into the type and look for VLA expressions. + do { + const Type *Ty = T.getTypePtr(); + switch (Ty->getTypeClass()) { +#define TYPE(Class, Base) +#define ABSTRACT_TYPE(Class, Base) +#define NON_CANONICAL_TYPE(Class, Base) +#define DEPENDENT_TYPE(Class, Base) case Type::Class: +#define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class, Base) +#include "clang/AST/TypeNodes.def" + T = QualType(); + break; + // These types are never variably-modified. + case Type::Builtin: + case Type::Complex: + case Type::Vector: + case Type::ExtVector: + case Type::Record: + case Type::Enum: + case Type::Elaborated: + case Type::TemplateSpecialization: + case Type::ObjCObject: + case Type::ObjCInterface: + case Type::ObjCObjectPointer: + case Type::Pipe: + llvm_unreachable("type class is never variably-modified!"); + case Type::Adjusted: + T = cast(Ty)->getOriginalType(); + break; + case Type::Decayed: + T = cast(Ty)->getPointeeType(); + break; + case Type::Pointer: + T = cast(Ty)->getPointeeType(); + break; + case Type::BlockPointer: + T = cast(Ty)->getPointeeType(); + break; + case Type::LValueReference: + case Type::RValueReference: + T = cast(Ty)->getPointeeType(); + break; + case Type::MemberPointer: + T = cast(Ty)->getPointeeType(); + break; + case Type::ConstantArray: + case Type::IncompleteArray: + // Losing element qualification here is fine. + T = cast(Ty)->getElementType(); + break; + case Type::VariableArray: { + // Losing element qualification here is fine. + const VariableArrayType *VAT = cast(Ty); + + // Unknown size indication requires no size computation. + // Otherwise, evaluate and record it. + if (auto Size = VAT->getSizeExpr()) { + if (!CSI->isVLATypeCaptured(VAT)) { + RecordDecl *CapRecord = nullptr; + if (auto LSI = dyn_cast(CSI)) { + CapRecord = LSI->Lambda; + } else if (auto CRSI = dyn_cast(CSI)) { + CapRecord = CRSI->TheRecordDecl; + } + if (CapRecord) { + auto ExprLoc = Size->getExprLoc(); + auto SizeType = Context.getSizeType(); + // Build the non-static data member. + auto Field = + FieldDecl::Create(Context, CapRecord, ExprLoc, ExprLoc, + /*Id*/ nullptr, SizeType, /*TInfo*/ nullptr, + /*BW*/ nullptr, /*Mutable*/ false, + /*InitStyle*/ ICIS_NoInit); + Field->setImplicit(true); + Field->setAccess(AS_private); + Field->setCapturedVLAType(VAT); + CapRecord->addDecl(Field); + + CSI->addVLATypeCapture(ExprLoc, SizeType); + } + } + } + T = VAT->getElementType(); + break; + } + case Type::FunctionProto: + case Type::FunctionNoProto: + T = cast(Ty)->getReturnType(); + break; + case Type::Paren: + case Type::TypeOf: + case Type::UnaryTransform: + case Type::Attributed: + case Type::SubstTemplateTypeParm: + case Type::PackExpansion: + // Keep walking after single level desugaring. + T = T.getSingleStepDesugaredType(Context); + break; + case Type::Typedef: + T = cast(Ty)->desugar(); + break; + case Type::Decltype: + T = cast(Ty)->desugar(); + break; + case Type::Auto: + T = cast(Ty)->getDeducedType(); + break; + case Type::TypeOfExpr: + T = cast(Ty)->getUnderlyingExpr()->getType(); + break; + case Type::Atomic: + T = cast(Ty)->getValueType(); + break; + } + } while (!T.isNull() && T->isVariablyModifiedType()); +} + /// \brief Build a sizeof or alignof expression given a type operand. ExprResult Sema::CreateUnaryExprOrTypeTraitExpr(TypeSourceInfo *TInfo, @@ -3763,6 +3885,20 @@ Sema::CreateUnaryExprOrTypeTraitExpr(TypeSourceInfo *TInfo, CheckUnaryExprOrTypeTraitOperand(T, OpLoc, R, ExprKind)) return ExprError(); + if (T->isVariablyModifiedType() && FunctionScopes.size() > 1) { + if (auto *TT = T->getAs()) { + if (auto *CSI = dyn_cast(FunctionScopes.back())) { + DeclContext *DC = nullptr; + if (auto LSI = dyn_cast(CSI)) + DC = LSI->CallOperator; + else if (auto CRSI = dyn_cast(CSI)) + DC = CRSI->TheCapturedDecl; + if (DC && TT->getDecl()->getDeclContext() != DC) + captureVariablyModifiedType(Context, T, CSI); + } + } + } + // C99 6.5.3.4p4: the type (an unsigned integer type) is size_t. return new (Context) UnaryExprOrTypeTraitExpr( ExprKind, TInfo, Context.getSizeType(), OpLoc, R.getEnd()); @@ -13150,120 +13286,7 @@ bool Sema::tryCaptureVariable( QualType QTy = Var->getType(); if (ParmVarDecl *PVD = dyn_cast_or_null(Var)) QTy = PVD->getOriginalType(); - do { - const Type *Ty = QTy.getTypePtr(); - switch (Ty->getTypeClass()) { -#define TYPE(Class, Base) -#define ABSTRACT_TYPE(Class, Base) -#define NON_CANONICAL_TYPE(Class, Base) -#define DEPENDENT_TYPE(Class, Base) case Type::Class: -#define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class, Base) -#include "clang/AST/TypeNodes.def" - QTy = QualType(); - break; - // These types are never variably-modified. - case Type::Builtin: - case Type::Complex: - case Type::Vector: - case Type::ExtVector: - case Type::Record: - case Type::Enum: - case Type::Elaborated: - case Type::TemplateSpecialization: - case Type::ObjCObject: - case Type::ObjCInterface: - case Type::ObjCObjectPointer: - case Type::Pipe: - llvm_unreachable("type class is never variably-modified!"); - case Type::Adjusted: - QTy = cast(Ty)->getOriginalType(); - break; - case Type::Decayed: - QTy = cast(Ty)->getPointeeType(); - break; - case Type::Pointer: - QTy = cast(Ty)->getPointeeType(); - break; - case Type::BlockPointer: - QTy = cast(Ty)->getPointeeType(); - break; - case Type::LValueReference: - case Type::RValueReference: - QTy = cast(Ty)->getPointeeType(); - break; - case Type::MemberPointer: - QTy = cast(Ty)->getPointeeType(); - break; - case Type::ConstantArray: - case Type::IncompleteArray: - // Losing element qualification here is fine. - QTy = cast(Ty)->getElementType(); - break; - case Type::VariableArray: { - // Losing element qualification here is fine. - const VariableArrayType *VAT = cast(Ty); - - // Unknown size indication requires no size computation. - // Otherwise, evaluate and record it. - if (auto Size = VAT->getSizeExpr()) { - if (!CSI->isVLATypeCaptured(VAT)) { - RecordDecl *CapRecord = nullptr; - if (auto LSI = dyn_cast(CSI)) { - CapRecord = LSI->Lambda; - } else if (auto CRSI = dyn_cast(CSI)) { - CapRecord = CRSI->TheRecordDecl; - } - if (CapRecord) { - auto ExprLoc = Size->getExprLoc(); - auto SizeType = Context.getSizeType(); - // Build the non-static data member. - auto Field = FieldDecl::Create( - Context, CapRecord, ExprLoc, ExprLoc, - /*Id*/ nullptr, SizeType, /*TInfo*/ nullptr, - /*BW*/ nullptr, /*Mutable*/ false, - /*InitStyle*/ ICIS_NoInit); - Field->setImplicit(true); - Field->setAccess(AS_private); - Field->setCapturedVLAType(VAT); - CapRecord->addDecl(Field); - - CSI->addVLATypeCapture(ExprLoc, SizeType); - } - } - } - QTy = VAT->getElementType(); - break; - } - case Type::FunctionProto: - case Type::FunctionNoProto: - QTy = cast(Ty)->getReturnType(); - break; - case Type::Paren: - case Type::TypeOf: - case Type::UnaryTransform: - case Type::Attributed: - case Type::SubstTemplateTypeParm: - case Type::PackExpansion: - // Keep walking after single level desugaring. - QTy = QTy.getSingleStepDesugaredType(getASTContext()); - break; - case Type::Typedef: - QTy = cast(Ty)->desugar(); - break; - case Type::Decltype: - QTy = cast(Ty)->desugar(); - break; - case Type::Auto: - QTy = cast(Ty)->getDeducedType(); - break; - case Type::TypeOfExpr: - QTy = cast(Ty)->getUnderlyingExpr()->getType(); - break; - case Type::Atomic: - QTy = cast(Ty)->getValueType(); - break; - } - } while (!QTy.isNull() && QTy->isVariablyModifiedType()); + captureVariablyModifiedType(Context, QTy, CSI); } if (getLangOpts().OpenMP) { diff --git a/test/CodeGenCXX/lambda-expressions.cpp b/test/CodeGenCXX/lambda-expressions.cpp index 2ea0561f9e9..4df44f4c5f7 100644 --- a/test/CodeGenCXX/lambda-expressions.cpp +++ b/test/CodeGenCXX/lambda-expressions.cpp @@ -10,10 +10,23 @@ void *use = &used; // CHECK: @cvar = global extern "C" auto cvar = []{}; +// CHECK-LABEL: define i32 @_Z9ARBSizeOfi(i32 +int ARBSizeOf(int n) { + typedef double (T)[8][n]; + using TT = double [8][n]; + return [&]() -> int { + typedef double(T1)[8][n]; + using TT1 = double[8][n]; + return sizeof(T) + sizeof(T1) + sizeof(TT) + sizeof(TT1); + }(); +} + +// CHECK-LABEL: define internal i32 @"_ZZ9ARBSizeOfiENK3$_0clEv" + int a() { return []{ return 1; }(); } // CHECK-LABEL: define i32 @_Z1av -// CHECK: call i32 @"_ZZ1avENK3$_0clEv" -// CHECK-LABEL: define internal i32 @"_ZZ1avENK3$_0clEv" +// CHECK: call i32 @"_ZZ1avENK3$_1clEv" +// CHECK-LABEL: define internal i32 @"_ZZ1avENK3$_1clEv" // CHECK: ret i32 1 int b(int x) { return [x]{return x;}(); } @@ -21,8 +34,8 @@ int b(int x) { return [x]{return x;}(); } // CHECK: store i32 // CHECK: load i32, i32* // CHECK: store i32 -// CHECK: call i32 @"_ZZ1biENK3$_1clEv" -// CHECK-LABEL: define internal i32 @"_ZZ1biENK3$_1clEv" +// CHECK: call i32 @"_ZZ1biENK3$_2clEv" +// CHECK-LABEL: define internal i32 @"_ZZ1biENK3$_2clEv" // CHECK: load i32, i32* // CHECK: ret i32 @@ -30,8 +43,8 @@ int c(int x) { return [&x]{return x;}(); } // CHECK-LABEL: define i32 @_Z1ci // CHECK: store i32 // CHECK: store i32* -// CHECK: call i32 @"_ZZ1ciENK3$_2clEv" -// CHECK-LABEL: define internal i32 @"_ZZ1ciENK3$_2clEv" +// CHECK: call i32 @"_ZZ1ciENK3$_3clEv" +// CHECK-LABEL: define internal i32 @"_ZZ1ciENK3$_3clEv" // CHECK: load i32*, i32** // CHECK: load i32, i32* // CHECK: ret i32 @@ -43,8 +56,8 @@ int d(int x) { D y[10]; [x,y] { return y[x].x; }(); } // CHECK: call void @_ZN1DC1Ev // CHECK: icmp ult i64 %{{.*}}, 10 // CHECK: call void @_ZN1DC1ERKS_ -// CHECK: call i32 @"_ZZ1diENK3$_3clEv" -// CHECK-LABEL: define internal i32 @"_ZZ1diENK3$_3clEv" +// CHECK: call i32 @"_ZZ1diENK3$_4clEv" +// CHECK-LABEL: define internal i32 @"_ZZ1diENK3$_4clEv" // CHECK: load i32, i32* // CHECK: load i32, i32* // CHECK: ret i32 @@ -54,18 +67,18 @@ int e(E a, E b, bool cond) { [a,b,cond](){ return (cond ? a : b).x; }(); } // CHECK-LABEL: define i32 @_Z1e1ES_b // CHECK: call void @_ZN1EC1ERKS_ // CHECK: invoke void @_ZN1EC1ERKS_ -// CHECK: invoke i32 @"_ZZ1e1ES_bENK3$_4clEv" -// CHECK: call void @"_ZZ1e1ES_bEN3$_4D1Ev" -// CHECK: call void @"_ZZ1e1ES_bEN3$_4D1Ev" +// CHECK: invoke i32 @"_ZZ1e1ES_bENK3$_5clEv" +// CHECK: call void @"_ZZ1e1ES_bEN3$_5D1Ev" +// CHECK: call void @"_ZZ1e1ES_bEN3$_5D1Ev" -// CHECK-LABEL: define internal i32 @"_ZZ1e1ES_bENK3$_4clEv" +// CHECK-LABEL: define internal i32 @"_ZZ1e1ES_bENK3$_5clEv" // CHECK: trunc i8 // CHECK: load i32, i32* // CHECK: ret i32 void f() { // CHECK-LABEL: define void @_Z1fv() - // CHECK: @"_ZZ1fvENK3$_5cvPFiiiEEv" + // CHECK: @"_ZZ1fvENK3$_6cvPFiiiEEv" // CHECK-NEXT: store i32 (i32, i32)* // CHECK-NEXT: ret void int (*fp)(int, int) = [](int x, int y){ return x + y; }; @@ -74,7 +87,7 @@ void f() { static int k; int g() { int &r = k; - // CHECK-LABEL: define internal i32 @"_ZZ1gvENK3$_6clEv"( + // CHECK-LABEL: define internal i32 @"_ZZ1gvENK3$_7clEv"( // CHECK-NOT: } // CHECK: load i32, i32* @_ZL1k, return [] { return r; } (); @@ -91,7 +104,7 @@ void staticarrayref(){ }(); } -// CHECK-LABEL: define internal i32* @"_ZZ11PR22071_funvENK3$_8clEv" +// CHECK-LABEL: define internal i32* @"_ZZ11PR22071_funvENK3$_9clEv" // CHECK: ret i32* @PR22071_var int PR22071_var; int *PR22071_fun() { @@ -99,19 +112,19 @@ int *PR22071_fun() { return [&] { return &y; }(); } -// CHECK-LABEL: define internal void @"_ZZ1e1ES_bEN3$_4D2Ev" +// CHECK-LABEL: define internal void @"_ZZ1e1ES_bEN3$_5D2Ev" -// CHECK-LABEL: define internal i32 @"_ZZ1fvEN3$_58__invokeEii" +// CHECK-LABEL: define internal i32 @"_ZZ1fvEN3$_68__invokeEii" // CHECK: store i32 // CHECK-NEXT: store i32 // CHECK-NEXT: load i32, i32* // CHECK-NEXT: load i32, i32* -// CHECK-NEXT: call i32 @"_ZZ1fvENK3$_5clEii" +// CHECK-NEXT: call i32 @"_ZZ1fvENK3$_6clEii" // CHECK-NEXT: ret i32 -// CHECK-LABEL: define internal void @"_ZZ1hvEN3$_98__invokeEv"(%struct.A* noalias sret %agg.result) {{.*}} { +// CHECK-LABEL: define internal void @"_ZZ1hvEN4$_108__invokeEv"(%struct.A* noalias sret %agg.result) {{.*}} { // CHECK-NOT: = -// CHECK: call void @"_ZZ1hvENK3$_9clEv"(%struct.A* sret %agg.result, +// CHECK: call void @"_ZZ1hvENK4$_10clEv"(%struct.A* sret %agg.result, // CHECK-NEXT: ret void struct A { ~A(); }; void h() { From b73a7ddbdc8e35c9c369480b24a7cc9a613a2117 Mon Sep 17 00:00:00 2001 From: Vedant Kumar Date: Thu, 21 Jan 2016 19:25:35 +0000 Subject: [PATCH 031/742] [Coverage] Reduce complexity of adding function mapping records Replace a string append operation in addFunctionMappingRecord with a vector append. The existing behavior is quadratic in the worst case: this patch makes it linear. Differential Revision: http://reviews.llvm.org/D16395 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258424 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 62a6c88a53fa85906c88010d7c9af17cc623987f) --- lib/CodeGen/CoverageMappingGen.cpp | 9 ++++++--- lib/CodeGen/CoverageMappingGen.h | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/CodeGen/CoverageMappingGen.cpp b/lib/CodeGen/CoverageMappingGen.cpp index cfbf781f59c..9432a7ba78d 100644 --- a/lib/CodeGen/CoverageMappingGen.cpp +++ b/lib/CodeGen/CoverageMappingGen.cpp @@ -15,6 +15,7 @@ #include "CodeGenFunction.h" #include "clang/AST/StmtVisitor.h" #include "clang/Lex/Lexer.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Optional.h" #include "llvm/ProfileData/CoverageMapping.h" #include "llvm/ProfileData/CoverageMappingReader.h" @@ -932,7 +933,7 @@ void CoverageMappingModuleGen::addFunctionMappingRecord( if (!IsUsed) FunctionNames.push_back( llvm::ConstantExpr::getBitCast(NamePtr, llvm::Type::getInt8PtrTy(Ctx))); - CoverageMappings += CoverageMapping; + CoverageMappings.push_back(CoverageMapping); if (CGM.getCodeGenOpts().DumpCoverageMapping) { // Dump the coverage mapping data for this function by decoding the @@ -978,8 +979,10 @@ void CoverageMappingModuleGen::emit() { std::string FilenamesAndCoverageMappings; llvm::raw_string_ostream OS(FilenamesAndCoverageMappings); CoverageFilenamesSectionWriter(FilenameRefs).write(OS); - OS << CoverageMappings; - size_t CoverageMappingSize = CoverageMappings.size(); + std::string RawCoverageMappings = + llvm::join(CoverageMappings.begin(), CoverageMappings.end(), ""); + OS << RawCoverageMappings; + size_t CoverageMappingSize = RawCoverageMappings.size(); size_t FilenamesSize = OS.str().size() - CoverageMappingSize; // Append extra zeroes if necessary to ensure that the size of the filenames // and coverage mappings is a multiple of 8. diff --git a/lib/CodeGen/CoverageMappingGen.h b/lib/CodeGen/CoverageMappingGen.h index 70aed842924..c202fe89934 100644 --- a/lib/CodeGen/CoverageMappingGen.h +++ b/lib/CodeGen/CoverageMappingGen.h @@ -56,7 +56,7 @@ class CoverageMappingModuleGen { std::vector FunctionRecords; std::vector FunctionNames; llvm::StructType *FunctionRecordTy; - std::string CoverageMappings; + std::vector CoverageMappings; public: CoverageMappingModuleGen(CodeGenModule &CGM, CoverageSourceInfo &SourceInfo) From 46f3159a0de89163dcf0e457267a87e741170f45 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Thu, 21 Jan 2016 20:09:49 +0000 Subject: [PATCH 032/742] [driver] Enable some static analyzer "unix" checkers on Windows. Many of the "unix" checkers are not actually unix-specific and would be valuable to run on Windows. This commit explicitly enables those checkers on Windows. A patch by Alexander Riccio! Differential Revision: http://reviews.llvm.org/D16245 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258426 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 497108d3a73d1b7ce538a0d9fd6d2c07c55d5579) --- lib/Driver/Tools.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index b906513d785..1d07fa0c045 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -3643,8 +3643,17 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, if (!Args.hasArg(options::OPT__analyzer_no_default_checks)) { CmdArgs.push_back("-analyzer-checker=core"); - if (!IsWindowsMSVC) - CmdArgs.push_back("-analyzer-checker=unix"); + if (!IsWindowsMSVC) { + CmdArgs.push_back("-analyzer-checker=unix"); + } else { + // Enable "unix" checkers that also work on Windows. + CmdArgs.push_back("-analyzer-checker=unix.API"); + CmdArgs.push_back("-analyzer-checker=unix.Malloc"); + CmdArgs.push_back("-analyzer-checker=unix.MallocSizeof"); + CmdArgs.push_back("-analyzer-checker=unix.MismatchedDeallocator"); + CmdArgs.push_back("-analyzer-checker=unix.cstring.BadSizeArg"); + CmdArgs.push_back("-analyzer-checker=unix.cstring.NullArg"); + } // Disable some unix checkers for PS4. if (IsPS4CPU) { From 005287670a0693097bd6c979a1064255b6954c25 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Fri, 22 Jan 2016 01:01:11 +0000 Subject: [PATCH 033/742] [analyzer] Suppress nullability warning for defensive super initializer idiom. A common idiom in Objective-C initializers is for a defensive nil-check on the result of a call to a super initializer: if (self = [super init]) { ... } return self; To avoid warning on this idiom, the nullability checker now suppress diagnostics for returns of nil on syntactic 'return self' even in initializers with non-null return types. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258461 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit cd1a0ca84198b972e782f9e3f7827710d9c2573d) --- .../Checkers/NullabilityChecker.cpp | 37 +++++++++++-- test/Analysis/nullability.mm | 52 ++++++++++++++++++- 2 files changed, 84 insertions(+), 5 deletions(-) diff --git a/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp index b532deb5533..079a71e0174 100644 --- a/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp @@ -470,6 +470,22 @@ static const Expr *lookThroughImplicitCasts(const Expr *E) { return E; } +/// Returns true when the return statement is a syntactic 'return self' in +/// Objective-C. +static bool isReturnSelf(const ReturnStmt *RS, CheckerContext &C) { + const ImplicitParamDecl *SelfDecl = + C.getCurrentAnalysisDeclContext()->getSelfDecl(); + if (!SelfDecl) + return false; + + const Expr *ReturnExpr = lookThroughImplicitCasts(RS->getRetValue()); + auto *RefExpr = dyn_cast(ReturnExpr); + if (!RefExpr) + return false; + + return RefExpr->getDecl() == SelfDecl; +} + /// This method check when nullable pointer or null value is returned from a /// function that has nonnull return type. /// @@ -494,16 +510,28 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S, if (!RetSVal) return; + bool IsReturnSelfInObjCInit = false; + QualType RequiredRetType; AnalysisDeclContext *DeclCtxt = C.getLocationContext()->getAnalysisDeclContext(); const Decl *D = DeclCtxt->getDecl(); - if (auto *MD = dyn_cast(D)) + if (auto *MD = dyn_cast(D)) { RequiredRetType = MD->getReturnType(); - else if (auto *FD = dyn_cast(D)) + // Suppress diagnostics for returns of nil that are syntactic returns of + // self in ObjC initializers. This avoids warning under the common idiom of + // a defensive check of the result of a call to super: + // if (self = [super init]) { + // ... + // } + // return self; // no-warning + IsReturnSelfInObjCInit = (MD->getMethodFamily() == OMF_init) && + isReturnSelf(S, C); + } else if (auto *FD = dyn_cast(D)) { RequiredRetType = FD->getReturnType(); - else + } else { return; + } NullConstraint Nullness = getNullConstraint(*RetSVal, State); @@ -520,7 +548,8 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S, if (Filter.CheckNullReturnedFromNonnull && Nullness == NullConstraint::IsNull && RetExprTypeLevelNullability != Nullability::Nonnull && - RequiredNullability == Nullability::Nonnull) { + RequiredNullability == Nullability::Nonnull && + !IsReturnSelfInObjCInit) { static CheckerProgramPointTag Tag(this, "NullReturnedFromNonnull"); ExplodedNode *N = C.generateErrorNode(State, &Tag); if (!N) diff --git a/test/Analysis/nullability.mm b/test/Analysis/nullability.mm index aa909fbb8c4..b119e63ffe2 100644 --- a/test/Analysis/nullability.mm +++ b/test/Analysis/nullability.mm @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -fobjc-arc -analyze -analyzer-checker=core,nullability -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,nullability -verify %s #define nil 0 #define BOOL int @@ -279,10 +280,21 @@ void inlinedUnspecified(Dummy *p) { return p; } -@interface SomeClass : NSObject + +@interface SomeClass : NSObject { + int instanceVar; +} @end @implementation SomeClass (MethodReturn) +- (id)initWithSomething:(int)i { + if (self = [super init]) { + instanceVar = i; + } + + return self; +} + - (TestObject * _Nonnull)testReturnsNullableInNonnullIndirectly { TestObject *local = getNullableTestObject(); return local; // expected-warning {{Nullable pointer is returned from a function that is expected to return a non-null value}} @@ -301,3 +313,41 @@ - (TestObject * _Nonnull)testReturnsNullableInNonnullWhenPreconditionViolated:(T return p; // no-warning } @end + +@interface ClassWithInitializers : NSObject +@end + +@implementation ClassWithInitializers +- (instancetype _Nonnull)initWithNonnullReturnAndSelfCheckingIdiom { + // This defensive check is a common-enough idiom that we filter don't want + // to issue a diagnostic for it, + if (self = [super init]) { + } + + return self; // no-warning +} + +- (instancetype _Nonnull)initWithNonnullReturnAndNilReturnViaLocal { + self = [super init]; + // This leaks, but we're not checking for that here. + + ClassWithInitializers *other = nil; + // Still warn when when not returning via self. + return other; // expected-warning {{Null is returned from a function that is expected to return a non-null value}} +} +@end + +@interface SubClassWithInitializers : ClassWithInitializers +@end + +@implementation SubClassWithInitializers +// Note: Because this is overridding +// -[ClassWithInitializers initWithNonnullReturnAndSelfCheckingIdiom], +// the return type of this method becomes implicitly id _Nonnull. +- (id)initWithNonnullReturnAndSelfCheckingIdiom { + if (self = [super initWithNonnullReturnAndSelfCheckingIdiom]) { + } + + return self; // no-warning +} +@end From cb9dc1181b343ac6a3a935762dbb05628060a18f Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Fri, 22 Jan 2016 07:08:06 +0000 Subject: [PATCH 034/742] [analyzer] Update SATestBuild.py to handle spaces in paths. The Jenkins workspace on the new Green Dragon builder for the static analyzer has spaces in its path. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258493 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 01a74c4d2ddf7e92e8fb2f9de3a7a823840df0a2) --- utils/analyzer/SATestBuild.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/utils/analyzer/SATestBuild.py b/utils/analyzer/SATestBuild.py index d0503c6389c..913b77bbf41 100755 --- a/utils/analyzer/SATestBuild.py +++ b/utils/analyzer/SATestBuild.py @@ -218,11 +218,11 @@ def runScript(ScriptPath, PBuildLogFile, Cwd): try: if Verbose == 1: print " Executing: %s" % (ScriptPath,) - check_call("chmod +x %s" % ScriptPath, cwd = Cwd, + check_call("chmod +x '%s'" % ScriptPath, cwd = Cwd, stderr=PBuildLogFile, stdout=PBuildLogFile, shell=True) - check_call(ScriptPath, cwd = Cwd, stderr=PBuildLogFile, + check_call("'%s'" % ScriptPath, cwd = Cwd, stderr=PBuildLogFile, stdout=PBuildLogFile, shell=True) except: @@ -261,7 +261,7 @@ def applyPatch(Dir, PBuildLogFile): print " Applying patch." try: - check_call("patch -p1 < %s" % (PatchfilePath), + check_call("patch -p1 < '%s'" % (PatchfilePath), cwd = PatchedSourceDirPath, stderr=PBuildLogFile, stdout=PBuildLogFile, @@ -286,7 +286,7 @@ def runScanBuild(Dir, SBOutputDir, PBuildLogFile): SBCwd = os.path.join(Dir, PatchedSourceDirName) SBOptions = "--use-analyzer " + Clang + " " - SBOptions += "-plist-html -o " + SBOutputDir + " " + SBOptions += "-plist-html -o '" + SBOutputDir + "' " SBOptions += "-enable-checker " + AllCheckers + " " SBOptions += "--keep-empty " # Always use ccc-analyze to ensure that we can locate the failures @@ -376,8 +376,8 @@ def runAnalyzePreprocessed(Dir, SBOutputDir, Mode): raise Exception() # Build and call the analyzer command. - OutputOption = "-o " + os.path.join(PlistPath, FileName) + ".plist " - Command = CmdPrefix + OutputOption + FileName + OutputOption = "-o '%s.plist' " % os.path.join(PlistPath, FileName) + Command = CmdPrefix + OutputOption + ("'%s'" % FileName) LogFile = open(os.path.join(FailPath, FileName + ".stderr.txt"), "w+b") try: if Verbose == 1: @@ -406,14 +406,14 @@ def buildProject(Dir, SBOutputDir, ProjectBuildMode, IsReferenceBuild): # Clean up the log file. if (os.path.exists(BuildLogPath)) : - RmCommand = "rm " + BuildLogPath + RmCommand = "rm '%s'" % BuildLogPath if Verbose == 1: print " Executing: %s" % (RmCommand,) check_call(RmCommand, shell=True) # Clean up scan build results. if (os.path.exists(SBOutputDir)) : - RmCommand = "rm -r " + SBOutputDir + RmCommand = "rm -r '%s'" % SBOutputDir if Verbose == 1: print " Executing: %s" % (RmCommand,) check_call(RmCommand, shell=True) @@ -593,9 +593,9 @@ def updateSVN(Mode, ProjectsMap): Path = os.path.join(ProjName, getSBOutputDirName(True)) if Mode == "delete": - Command = "svn delete %s" % (Path,) + Command = "svn delete '%s'" % (Path,) else: - Command = "svn add %s" % (Path,) + Command = "svn add '%s'" % (Path,) if Verbose == 1: print " Executing: %s" % (Command,) From e24b1e563f8989512af51cd104d11f21bf69f006 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Fri, 22 Jan 2016 17:43:43 +0000 Subject: [PATCH 035/742] Module Debugging: Use a nonzero DWO id for precompiled headers. PCH files don't have a module signature and LLVM uses a nonzero DWO id as an indicator for skeleton / module CUs. This change pins the DWO id for PCH files to a known constant value. The correct long-term solution here is to implement a module signature that is an actual dterministic hash (at the moment module signatures are just random nonzero numbers) and then enable this for PCH files as well. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258507 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit a892974a4f1b55ce481c33b0250dbc07a37486c1) --- lib/CodeGen/CGDebugInfo.cpp | 5 ++++- lib/CodeGen/ObjectFilePCHContainerOperations.cpp | 6 +++++- test/Modules/ExtDebugInfo.cpp | 4 ++++ test/Modules/ModuleDebugInfo.cpp | 3 +-- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index 28e28489681..4e0cc3f8574 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -1740,11 +1740,14 @@ CGDebugInfo::getOrCreateModuleRef(ExternalASTSource::ASTSourceDescriptor Mod, bool IsRootModule = M ? !M->Parent : true; if (CreateSkeletonCU && IsRootModule) { + // PCH files don't have a signature field in the control block, + // but LLVM detects skeleton CUs by looking for a non-zero DWO id. + uint64_t Signature = Mod.getSignature() ? Mod.getSignature() : ~1U; llvm::DIBuilder DIB(CGM.getModule()); DIB.createCompileUnit(TheCU->getSourceLanguage(), Mod.getModuleName(), Mod.getPath(), TheCU->getProducer(), true, StringRef(), 0, Mod.getASTFile(), - llvm::DIBuilder::FullDebug, Mod.getSignature()); + llvm::DIBuilder::FullDebug, Signature); DIB.finalize(); } llvm::DIModule *Parent = diff --git a/lib/CodeGen/ObjectFilePCHContainerOperations.cpp b/lib/CodeGen/ObjectFilePCHContainerOperations.cpp index cd17832554d..2178ec21c7a 100644 --- a/lib/CodeGen/ObjectFilePCHContainerOperations.cpp +++ b/lib/CodeGen/ObjectFilePCHContainerOperations.cpp @@ -219,7 +219,11 @@ class PCHContainerGenerator : public ASTConsumer { M->setTargetTriple(Ctx.getTargetInfo().getTriple().getTriple()); M->setDataLayout(Ctx.getTargetInfo().getDataLayoutString()); - Builder->getModuleDebugInfo()->setDwoId(Buffer->Signature); + + // PCH files don't have a signature field in the control block, + // but LLVM detects DWO CUs by looking for a non-zero DWO id. + uint64_t Signature = Buffer->Signature ? Buffer->Signature : ~1U; + Builder->getModuleDebugInfo()->setDwoId(Signature); // Finalize the Builder. if (Builder) diff --git a/test/Modules/ExtDebugInfo.cpp b/test/Modules/ExtDebugInfo.cpp index b9eae12deda..c4c6a298234 100644 --- a/test/Modules/ExtDebugInfo.cpp +++ b/test/Modules/ExtDebugInfo.cpp @@ -102,3 +102,7 @@ void foo() { // CHECK: !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !0, entity: !"_ZTSN8DebugCXX6StructE", line: 24) + +// CHECK: !DICompileUnit( +// CHECK-SAME: splitDebugFilename: +// CHECK-SAME: dwoId: diff --git a/test/Modules/ModuleDebugInfo.cpp b/test/Modules/ModuleDebugInfo.cpp index a2b735b3b70..73443972409 100644 --- a/test/Modules/ModuleDebugInfo.cpp +++ b/test/Modules/ModuleDebugInfo.cpp @@ -8,7 +8,6 @@ // RUN: %clang_cc1 -triple %itanium_abi_triple -x objective-c++ -std=c++11 -debug-info-kind=limited -fmodules -fmodule-format=obj -fimplicit-module-maps -DMODULES -fmodules-cache-path=%t %s -I %S/Inputs -I %t -emit-llvm -o %t.ll -mllvm -debug-only=pchcontainer &>%t-mod.ll // RUN: cat %t-mod.ll | FileCheck %s // RUN: cat %t-mod.ll | FileCheck --check-prefix=CHECK-NEG %s -// RUN: cat %t-mod.ll | FileCheck --check-prefix=CHECK-DWO %s // PCH: // RUN: %clang_cc1 -triple %itanium_abi_triple -x c++ -std=c++11 -emit-pch -fmodule-format=obj -I %S/Inputs -o %t.pch %S/Inputs/DebugCXX.h -mllvm -debug-only=pchcontainer &>%t-pch.ll @@ -22,7 +21,7 @@ // CHECK: distinct !DICompileUnit(language: DW_LANG_{{.*}}C_plus_plus, // CHECK-SAME: isOptimized: false, // CHECK-SAME-NOT: splitDebugFilename: -// CHECK-DWO: dwoId: +// CHECK: dwoId: // CHECK: !DICompositeType(tag: DW_TAG_enumeration_type, name: "Enum" // CHECK-SAME: identifier: "_ZTSN8DebugCXX4EnumE") From 7f5cfd295703baa59caa4e9087a04cc53dc616b2 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Fri, 22 Jan 2016 18:45:22 +0000 Subject: [PATCH 036/742] [analyzer] SATestBuild.py: Handle spaces in path passed to --use-analyzer. I missed this one in r258493. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258517 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 84b659738b0e151d63a1c4e6c264527635ba8f17) --- utils/analyzer/SATestBuild.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/analyzer/SATestBuild.py b/utils/analyzer/SATestBuild.py index 913b77bbf41..9c830933728 100755 --- a/utils/analyzer/SATestBuild.py +++ b/utils/analyzer/SATestBuild.py @@ -285,8 +285,8 @@ def runScanBuild(Dir, SBOutputDir, PBuildLogFile): # Run scan-build from within the patched source directory. SBCwd = os.path.join(Dir, PatchedSourceDirName) - SBOptions = "--use-analyzer " + Clang + " " - SBOptions += "-plist-html -o '" + SBOutputDir + "' " + SBOptions = "--use-analyzer '%s' " % Clang + SBOptions += "-plist-html -o '%s' " % SBOutputDir SBOptions += "-enable-checker " + AllCheckers + " " SBOptions += "--keep-empty " # Always use ccc-analyze to ensure that we can locate the failures From 38aa7b86d3a77dd7c90c4cdb8cb5b4b07436be06 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Fri, 22 Jan 2016 18:46:40 +0000 Subject: [PATCH 037/742] Fix 80-column violations. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258518 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit efaf7e0f2767ba608dcc1bd6fa306c15d0fac3bf) --- test/Modules/ExtDebugInfo.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/Modules/ExtDebugInfo.cpp b/test/Modules/ExtDebugInfo.cpp index c4c6a298234..189ebf016eb 100644 --- a/test/Modules/ExtDebugInfo.cpp +++ b/test/Modules/ExtDebugInfo.cpp @@ -2,7 +2,8 @@ // Test that only forward declarations are emitted for types dfined in modules. // Modules: -// RUN: %clang_cc1 -x objective-c++ -std=c++11 -debug-info-kind=limited -dwarf-ext-refs -fmodules \ +// RUN: %clang_cc1 -x objective-c++ -std=c++11 -debug-info-kind=limited \ +// RUN: -dwarf-ext-refs -fmodules \ // RUN: -fmodule-format=obj -fimplicit-module-maps -DMODULES \ // RUN: -triple %itanium_abi_triple \ // RUN: -fmodules-cache-path=%t %s -I %S/Inputs -I %t -emit-llvm -o %t-mod.ll @@ -12,11 +13,13 @@ // RUN: %clang_cc1 -x c++ -std=c++11 -fmodule-format=obj -emit-pch -I%S/Inputs \ // RUN: -triple %itanium_abi_triple \ // RUN: -o %t.pch %S/Inputs/DebugCXX.h -// RUN: %clang_cc1 -std=c++11 -debug-info-kind=limited -dwarf-ext-refs -fmodule-format=obj \ +// RUN: %clang_cc1 -std=c++11 -debug-info-kind=limited \ +// RUN: -dwarf-ext-refs -fmodule-format=obj \ // RUN: -triple %itanium_abi_triple \ // RUN: -include-pch %t.pch %s -emit-llvm -o %t-pch.ll %s // RUN: cat %t-pch.ll | FileCheck %s + #ifdef MODULES @import DebugCXX; #endif @@ -101,7 +104,7 @@ void foo() { // CHECK-SAME: name: "InAnonymousNamespace", {{.*}}DIFlagFwdDecl) -// CHECK: !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !0, entity: !"_ZTSN8DebugCXX6StructE", line: 24) +// CHECK: !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !0, entity: !"_ZTSN8DebugCXX6StructE", line: 27) // CHECK: !DICompileUnit( // CHECK-SAME: splitDebugFilename: From f7a71ab0fccdd12a20a5f7574355f7f503494b31 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Fri, 22 Jan 2016 18:46:45 +0000 Subject: [PATCH 038/742] Fix a typo in r258507 and change the PCH dwoid constant to ~1UL. rdar://problem/24290667 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258519 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 9fd9596d49dd54b3075c8335867d9a7f69415a69) --- lib/CodeGen/CGDebugInfo.cpp | 2 +- lib/CodeGen/ObjectFilePCHContainerOperations.cpp | 2 +- test/Modules/ExtDebugInfo.cpp | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index 4e0cc3f8574..635a2d06a24 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -1742,7 +1742,7 @@ CGDebugInfo::getOrCreateModuleRef(ExternalASTSource::ASTSourceDescriptor Mod, if (CreateSkeletonCU && IsRootModule) { // PCH files don't have a signature field in the control block, // but LLVM detects skeleton CUs by looking for a non-zero DWO id. - uint64_t Signature = Mod.getSignature() ? Mod.getSignature() : ~1U; + uint64_t Signature = Mod.getSignature() ? Mod.getSignature() : ~1UL; llvm::DIBuilder DIB(CGM.getModule()); DIB.createCompileUnit(TheCU->getSourceLanguage(), Mod.getModuleName(), Mod.getPath(), TheCU->getProducer(), true, diff --git a/lib/CodeGen/ObjectFilePCHContainerOperations.cpp b/lib/CodeGen/ObjectFilePCHContainerOperations.cpp index 2178ec21c7a..f20489275b2 100644 --- a/lib/CodeGen/ObjectFilePCHContainerOperations.cpp +++ b/lib/CodeGen/ObjectFilePCHContainerOperations.cpp @@ -222,7 +222,7 @@ class PCHContainerGenerator : public ASTConsumer { // PCH files don't have a signature field in the control block, // but LLVM detects DWO CUs by looking for a non-zero DWO id. - uint64_t Signature = Buffer->Signature ? Buffer->Signature : ~1U; + uint64_t Signature = Buffer->Signature ? Buffer->Signature : ~1UL; Builder->getModuleDebugInfo()->setDwoId(Signature); // Finalize the Builder. diff --git a/test/Modules/ExtDebugInfo.cpp b/test/Modules/ExtDebugInfo.cpp index 189ebf016eb..dfc8be53d1f 100644 --- a/test/Modules/ExtDebugInfo.cpp +++ b/test/Modules/ExtDebugInfo.cpp @@ -18,7 +18,7 @@ // RUN: -triple %itanium_abi_triple \ // RUN: -include-pch %t.pch %s -emit-llvm -o %t-pch.ll %s // RUN: cat %t-pch.ll | FileCheck %s - +// RUN: cat %t-pch.ll | FileCheck %s --check-prefix=CHECK-PCH #ifdef MODULES @import DebugCXX; @@ -109,3 +109,4 @@ void foo() { // CHECK: !DICompileUnit( // CHECK-SAME: splitDebugFilename: // CHECK-SAME: dwoId: +// CHECK-PCH: dwoId: 18446744073709551614 From a7893f4749f11684589ff0a26f87dd71dcf3c75d Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Fri, 22 Jan 2016 19:14:24 +0000 Subject: [PATCH 039/742] Rephrase this test to help debug a buildbot issue git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258526 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 2e0dd420505265b143d0399d06846ef6b0f652d2) --- test/Modules/ExtDebugInfo.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/Modules/ExtDebugInfo.cpp b/test/Modules/ExtDebugInfo.cpp index dfc8be53d1f..3b4547a87e0 100644 --- a/test/Modules/ExtDebugInfo.cpp +++ b/test/Modules/ExtDebugInfo.cpp @@ -109,4 +109,5 @@ void foo() { // CHECK: !DICompileUnit( // CHECK-SAME: splitDebugFilename: // CHECK-SAME: dwoId: -// CHECK-PCH: dwoId: 18446744073709551614 +// CHECK-PCH: !DICompileUnit({{.*}}splitDebugFilename: +// CHECK-PCH: dwoId: 18446744073709551614 From 7eeac8e0d2f625b3da4cafc8095cee4407b34d22 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Fri, 22 Jan 2016 19:29:41 +0000 Subject: [PATCH 040/742] Fix the build by using the correct suffix for 64 bit literals git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258531 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit c87ca354aab2ba8f95b925b886a04f78f9430bb4) --- lib/CodeGen/CGDebugInfo.cpp | 2 +- lib/CodeGen/ObjectFilePCHContainerOperations.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index 635a2d06a24..efacc35d149 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -1742,7 +1742,7 @@ CGDebugInfo::getOrCreateModuleRef(ExternalASTSource::ASTSourceDescriptor Mod, if (CreateSkeletonCU && IsRootModule) { // PCH files don't have a signature field in the control block, // but LLVM detects skeleton CUs by looking for a non-zero DWO id. - uint64_t Signature = Mod.getSignature() ? Mod.getSignature() : ~1UL; + uint64_t Signature = Mod.getSignature() ? Mod.getSignature() : ~1ULL; llvm::DIBuilder DIB(CGM.getModule()); DIB.createCompileUnit(TheCU->getSourceLanguage(), Mod.getModuleName(), Mod.getPath(), TheCU->getProducer(), true, diff --git a/lib/CodeGen/ObjectFilePCHContainerOperations.cpp b/lib/CodeGen/ObjectFilePCHContainerOperations.cpp index f20489275b2..0e3aa61314e 100644 --- a/lib/CodeGen/ObjectFilePCHContainerOperations.cpp +++ b/lib/CodeGen/ObjectFilePCHContainerOperations.cpp @@ -222,7 +222,7 @@ class PCHContainerGenerator : public ASTConsumer { // PCH files don't have a signature field in the control block, // but LLVM detects DWO CUs by looking for a non-zero DWO id. - uint64_t Signature = Buffer->Signature ? Buffer->Signature : ~1UL; + uint64_t Signature = Buffer->Signature ? Buffer->Signature : ~1ULL; Builder->getModuleDebugInfo()->setDwoId(Signature); // Finalize the Builder. From 63fd091bb59987a89c0037cf24e5dc3000f3d431 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Fri, 22 Jan 2016 21:14:41 +0000 Subject: [PATCH 041/742] Module debugging: Create a parent DIModule with the PCH name for types emitted into a precompiled header to mirror the debug info emitted for object files importing the PCH. rdar://problem/24290667 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258555 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit d97ae762ac6e2f11ed112b9b03eb26b5446531af) --- lib/CodeGen/CGDebugInfo.cpp | 4 + lib/CodeGen/CGDebugInfo.h | 12 +++ .../ObjectFilePCHContainerOperations.cpp | 6 +- test/Modules/ModuleDebugInfo.m | 95 ++++++++++--------- 4 files changed, 71 insertions(+), 46 deletions(-) diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index efacc35d149..675b5eb07c5 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -2224,8 +2224,12 @@ llvm::DIModule *CGDebugInfo::getParentModuleOrNull(const Decl *D) { // option. FullSourceLoc Loc(D->getLocation(), CGM.getContext().getSourceManager()); if (Module *M = ClangModuleMap->inferModuleFromLocation(Loc)) { + // This is a (sub-)module. auto Info = ExternalASTSource::ASTSourceDescriptor(*M); return getOrCreateModuleRef(Info, /*SkeletonCU=*/false); + } else { + // This the precompiled header being built. + return getOrCreateModuleRef(PCHDescriptor, /*SkeletonCU=*/false); } } diff --git a/lib/CodeGen/CGDebugInfo.h b/lib/CodeGen/CGDebugInfo.h index a68dd33fa5f..55d813834e8 100644 --- a/lib/CodeGen/CGDebugInfo.h +++ b/lib/CodeGen/CGDebugInfo.h @@ -16,6 +16,7 @@ #include "CGBuilder.h" #include "clang/AST/Expr.h" +#include "clang/AST/ExternalASTSource.h" #include "clang/AST/Type.h" #include "clang/Basic/SourceLocation.h" #include "clang/Frontend/CodeGenOptions.h" @@ -57,6 +58,7 @@ class CGDebugInfo { llvm::DIBuilder DBuilder; llvm::DICompileUnit *TheCU = nullptr; ModuleMap *ClangModuleMap = nullptr; + ExternalASTSource::ASTSourceDescriptor PCHDescriptor; SourceLocation CurLoc; llvm::DIType *VTablePtrType = nullptr; llvm::DIType *ClassTy = nullptr; @@ -275,6 +277,8 @@ class CGDebugInfo { void finalize(); + /// Module debugging: Support for building PCMs. + /// @{ /// Set the main CU's DwoId field to \p Signature. void setDwoId(uint64_t Signature); @@ -283,6 +287,14 @@ class CGDebugInfo { /// the module of origin of each Decl. void setModuleMap(ModuleMap &MMap) { ClangModuleMap = &MMap; } + /// When generating debug information for a clang module or + /// precompiled header, this module map will be used to determine + /// the module of origin of each Decl. + void setPCHDescriptor(ExternalASTSource::ASTSourceDescriptor PCH) { + PCHDescriptor = PCH; + } + /// @} + /// Update the current source location. If \arg loc is invalid it is /// ignored. void setLocation(SourceLocation Loc); diff --git a/lib/CodeGen/ObjectFilePCHContainerOperations.cpp b/lib/CodeGen/ObjectFilePCHContainerOperations.cpp index 0e3aa61314e..406b433c95c 100644 --- a/lib/CodeGen/ObjectFilePCHContainerOperations.cpp +++ b/lib/CodeGen/ObjectFilePCHContainerOperations.cpp @@ -42,6 +42,7 @@ namespace { class PCHContainerGenerator : public ASTConsumer { DiagnosticsEngine &Diags; const std::string MainFileName; + const std::string OutputFileName; ASTContext *Ctx; ModuleMap &MMap; const HeaderSearchOptions &HeaderSearchOpts; @@ -137,7 +138,8 @@ class PCHContainerGenerator : public ASTConsumer { const std::string &OutputFileName, raw_pwrite_stream *OS, std::shared_ptr Buffer) - : Diags(CI.getDiagnostics()), Ctx(nullptr), + : Diags(CI.getDiagnostics()), MainFileName(MainFileName), + OutputFileName(OutputFileName), Ctx(nullptr), MMap(CI.getPreprocessor().getHeaderSearchInfo().getModuleMap()), HeaderSearchOpts(CI.getHeaderSearchOpts()), PreprocessorOpts(CI.getPreprocessorOpts()), @@ -163,6 +165,8 @@ class PCHContainerGenerator : public ASTConsumer { Builder.reset(new CodeGen::CodeGenModule( *Ctx, HeaderSearchOpts, PreprocessorOpts, CodeGenOpts, *M, Diags)); Builder->getModuleDebugInfo()->setModuleMap(MMap); + Builder->getModuleDebugInfo()->setPCHDescriptor( + {MainFileName, "", OutputFileName, ~1ULL}); } bool HandleTopLevelDecl(DeclGroupRef D) override { diff --git a/test/Modules/ModuleDebugInfo.m b/test/Modules/ModuleDebugInfo.m index cba4063b1de..3d6c6657c91 100644 --- a/test/Modules/ModuleDebugInfo.m +++ b/test/Modules/ModuleDebugInfo.m @@ -10,13 +10,14 @@ // RUN: -I %S/Inputs -I %t -emit-llvm -o %t.ll \ // RUN: -mllvm -debug-only=pchcontainer &>%t-mod.ll // RUN: cat %t-mod.ll | FileCheck %s -// RUN: cat %t-mod.ll | FileCheck %s --check-prefix=MODULE-CHECK +// RUN: cat %t-mod.ll | FileCheck %s --check-prefix=CHECK2 // PCH: // RUN: %clang_cc1 -x objective-c -emit-pch -fmodule-format=obj -I %S/Inputs \ // RUN: -o %t.pch %S/Inputs/DebugObjC.h \ // RUN: -mllvm -debug-only=pchcontainer &>%t-pch.ll // RUN: cat %t-pch.ll | FileCheck %s +// RUN: cat %t-pch.ll | FileCheck %s --check-prefix=CHECK2 #ifdef MODULES @import DebugObjC; @@ -24,61 +25,65 @@ // CHECK: distinct !DICompileUnit(language: DW_LANG_ObjC // CHECK-SAME: isOptimized: false, + +// CHECK: !DICompositeType(tag: DW_TAG_enumeration_type, +// CHECK-SAME: scope: ![[MODULE:[0-9]+]], +// CHECK: ![[MODULE]] = !DIModule(scope: null, name:{{.*}}DebugObjC + +// CHECK: ![[TD_ENUM:.*]] = !DICompositeType(tag: DW_TAG_enumeration_type, +// CHECK-SAME-NOT: name: +// CHECK-SAME: elements: + // CHECK: !DICompositeType(tag: DW_TAG_structure_type, // CHECK-SAME: name: "FwdDecl", +// CHECK-SAME: scope: ![[MODULE]], // CHECK: !DICompositeType(tag: DW_TAG_structure_type, // CHECK-SAME: name: "ObjCClass", -// CHECK: !DIObjCProperty(name: "property", -// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "ivar" -// CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "InnerEnum" -// CHECK: !DISubprogram(name: "+[ObjCClass classMethod]" -// CHECK: !DISubprogram(name: "-[ObjCClass instanceMethodWithInt:]" -// CHECK: !DISubprogram(name: "-[Category(Category) categoryMethod]" - -// MODULE-CHECK: !DICompositeType(tag: DW_TAG_enumeration_type, -// MODULE-CHECK-SAME: scope: ![[MODULE:[0-9]+]], -// MODULE-CHECK: ![[MODULE]] = !DIModule(scope: null, name: "DebugObjC" +// CHECK-SAME: scope: ![[MODULE]], -// MODULE-CHECK: ![[TD_ENUM:.*]] = !DICompositeType(tag: DW_TAG_enumeration_type, -// MODULE-CHECK-SAME-NOT: name: -// MODULE-CHECK-SAME: elements: +// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "FwdDeclared" +// CHECK-SAME: elements: -// MODULE-CHECK: !DICompositeType(tag: DW_TAG_structure_type, -// MODULE-CHECK-SAME: name: "FwdDecl", -// MODULE-CHECK-SAME: scope: ![[MODULE]], -// MODULE-CHECK: !DICompositeType(tag: DW_TAG_structure_type, -// MODULE-CHECK-SAME: name: "ObjCClass", -// MODULE-CHECK-SAME: scope: ![[MODULE]], +// CHECK: ![[TD_UNION:.*]] = !DICompositeType(tag: DW_TAG_union_type, +// CHECK-SAME-NOT: name: +// CHECK-SAME: elements: -// MODULE-CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "FwdDeclared" -// MODULE-CHECK-SAME: elements: +// CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "TypedefUnion", +// CHECK-SAME: baseType: ![[TD_UNION]]) -// MODULE-CHECK: ![[TD_UNION:.*]] = !DICompositeType(tag: DW_TAG_union_type, -// MODULE-CHECK-SAME-NOT: name: -// MODULE-CHECK-SAME: elements: +// CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "TypedefEnum", +// CHECK-SAME: baseType: ![[TD_ENUM:.*]]) -// MODULE-CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "TypedefUnion", -// MODULE-CHECK-SAME: baseType: ![[TD_UNION]]) +// CHECK: ![[TD_STRUCT:.*]] = !DICompositeType(tag: DW_TAG_structure_type, +// CHECK-SAME-NOT: name: +// CHECK-SAME: elements: +// CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "TypedefStruct", +// CHECK-SAME: baseType: ![[TD_STRUCT]]) -// MODULE-CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "TypedefEnum", -// MODULE-CHECK-SAME: baseType: ![[TD_ENUM:.*]]) +// CHECK: !DICompositeType(tag: DW_TAG_union_type, +// CHECK-SAME-NOT: name: -// MODULE-CHECK: ![[TD_STRUCT:.*]] = !DICompositeType(tag: DW_TAG_structure_type, -// MODULE-CHECK-SAME-NOT: name: -// MODULE-CHECK-SAME: elements: -// MODULE-CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "TypedefStruct", -// MODULE-CHECK-SAME: baseType: ![[TD_STRUCT]]) - -// MODULE-CHECK: !DICompositeType(tag: DW_TAG_union_type, -// MODULE-CHECK-SAME-NOT: name: - -// MODULE-CHECK: !DICompositeType(tag: DW_TAG_structure_type, -// MODULE-CHECK-SAME-NOT: name: +// CHECK: !DICompositeType(tag: DW_TAG_structure_type, +// CHECK-SAME-NOT: name: -// MODULE-CHECK: !DISubprogram(name: "+[ObjCClass classMethod]", -// MODULE-CHECK-SAME: scope: ![[MODULE]], +// CHECK: !DISubprogram(name: "+[ObjCClass classMethod]", +// CHECK-SAME: scope: ![[MODULE]], // The forward declaration should not be in the module scope. -// MODULE-CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "OpaqueData", file - -// MODULE-CHECK-NEG-NOT: !DICompositeType(tag: DW_TAG_structure_type, name: "PureForwardDecl" +// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "OpaqueData", file + +// CHECK-NEG-NOT: !DICompositeType(tag: DW_TAG_structure_type, name: "PureForwardDecl" + +// The output order is sublty different for module vs. pch, +// so these are checked separately: +// +// CHECK2: !DICompositeType(tag: DW_TAG_structure_type, +// CHECK2-SAME: name: "FwdDecl", +// CHECK2: !DICompositeType(tag: DW_TAG_structure_type, +// CHECK2-SAME: name: "ObjCClass", +// CHECK2: !DIObjCProperty(name: "property", +// CHECK2: !DIDerivedType(tag: DW_TAG_member, name: "ivar" +// CHECK2: !DIDerivedType(tag: DW_TAG_typedef, name: "InnerEnum" +// CHECK2: !DISubprogram(name: "+[ObjCClass classMethod]" +// CHECK2: !DISubprogram(name: "-[ObjCClass instanceMethodWithInt:]" +// CHECK2: !DISubprogram(name: "-[Category(Category) categoryMethod]" From bc33e91aa833853a98dfda107c774f35b73b6c26 Mon Sep 17 00:00:00 2001 From: Gabor Horvath Date: Fri, 22 Jan 2016 22:32:46 +0000 Subject: [PATCH 042/742] [analyzer] Utility to match function calls. This patch adds a small utility to match function calls. This utility abstracts away the mutable keywords and the lazy initialization and caching logic of identifiers from the checkers. The SimpleStreamChecker is ported over this utility within this patch to show the reduction of code and to test this change. Differential Revision: http://reviews.llvm.org/D15921 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258572 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 1baedeccd6989bcf270f1fcd2e6b37d5dcb9f46e) --- .../Core/PathSensitive/CallEvent.h | 28 +++++++++++++++++++ .../Checkers/SimpleStreamChecker.cpp | 25 +++-------------- lib/StaticAnalyzer/Core/CallEvent.cpp | 10 +++++++ 3 files changed, 42 insertions(+), 21 deletions(-) diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h index b09dffaf4e5..a67204c3bd4 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -49,6 +49,27 @@ enum CallEventKind { class CallEvent; class CallEventManager; +/// This class represents a description of a function call using the number of +/// arguments and the name of the function. +class CallDescription { + friend CallEvent; + mutable IdentifierInfo *II; + StringRef FuncName; + unsigned RequiredArgs; + +public: + const static unsigned NoArgRequirement = ~0; + /// \brief Constructs a CallDescription object. + /// + /// @param FuncName The name of the function that will be matched. + /// + /// @param RequiredArgs The number of arguments that is expected to match a + /// call. Omit this parameter to match every occurance of call with a given + /// name regardless the number of arguments. + CallDescription(StringRef FuncName, unsigned RequiredArgs = NoArgRequirement) + : FuncName(FuncName), RequiredArgs(RequiredArgs) {} +}; + template class CallEventRef : public IntrusiveRefCntPtr { public: @@ -227,6 +248,13 @@ class CallEvent { return false; } + /// \brief Returns true if the CallEvent is a call to a function that matches + /// the CallDescription. + /// + /// Note that this function is not intended to be used to match Obj-C method + /// calls. + bool isCalled(const CallDescription &CD) const; + /// \brief Returns a source range for the entire call, suitable for /// outputting in diagnostics. virtual SourceRange getSourceRange() const { diff --git a/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp b/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp index 7026a2ec16a..62e6f02fe3b 100644 --- a/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp @@ -51,14 +51,11 @@ class SimpleStreamChecker : public Checker { - - mutable IdentifierInfo *IIfopen, *IIfclose; + CallDescription OpenFn, CloseFn; std::unique_ptr DoubleCloseBugType; std::unique_ptr LeakBugType; - void initIdentifierInfo(ASTContext &Ctx) const; - void reportDoubleClose(SymbolRef FileDescSym, const CallEvent &Call, CheckerContext &C) const; @@ -106,7 +103,7 @@ class StopTrackingCallback final : public SymbolVisitor { } // end anonymous namespace SimpleStreamChecker::SimpleStreamChecker() - : IIfopen(nullptr), IIfclose(nullptr) { + : OpenFn("fopen"), CloseFn("fclose", 1) { // Initialize the bug types. DoubleCloseBugType.reset( new BugType(this, "Double fclose", "Unix Stream API Error")); @@ -119,12 +116,10 @@ SimpleStreamChecker::SimpleStreamChecker() void SimpleStreamChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { - initIdentifierInfo(C.getASTContext()); - if (!Call.isGlobalCFunction()) return; - if (Call.getCalleeIdentifier() != IIfopen) + if (!Call.isCalled(OpenFn)) return; // Get the symbolic value corresponding to the file handle. @@ -140,15 +135,10 @@ void SimpleStreamChecker::checkPostCall(const CallEvent &Call, void SimpleStreamChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { - initIdentifierInfo(C.getASTContext()); - if (!Call.isGlobalCFunction()) return; - if (Call.getCalleeIdentifier() != IIfclose) - return; - - if (Call.getNumArgs() != 1) + if (!Call.isCalled(CloseFn)) return; // Get the symbolic value corresponding to the file handle. @@ -275,13 +265,6 @@ SimpleStreamChecker::checkPointerEscape(ProgramStateRef State, return State; } -void SimpleStreamChecker::initIdentifierInfo(ASTContext &Ctx) const { - if (IIfopen) - return; - IIfopen = &Ctx.Idents.get("fopen"); - IIfclose = &Ctx.Idents.get("fclose"); -} - void ento::registerSimpleStreamChecker(CheckerManager &mgr) { mgr.registerChecker(); } diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp index 69af09b25b6..59b90b5ce98 100644 --- a/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -210,6 +210,16 @@ ProgramPoint CallEvent::getProgramPoint(bool IsPreVisit, return PostImplicitCall(D, Loc, getLocationContext(), Tag); } +bool CallEvent::isCalled(const CallDescription &CD) const { + assert(getKind() != CE_ObjCMessage && "Obj-C methods are not supported"); + if (!CD.II) + CD.II = &getState()->getStateManager().getContext().Idents.get(CD.FuncName); + if (getCalleeIdentifier() != CD.II) + return false; + return (CD.RequiredArgs == CallDescription::NoArgRequirement || + CD.RequiredArgs == getNumArgs()); +} + SVal CallEvent::getArgSVal(unsigned Index) const { const Expr *ArgE = getArgExpr(Index); if (!ArgE) From ba5219c1b57b22c17ef4f7ab63e922a6777d7c8c Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Fri, 22 Jan 2016 23:30:56 +0000 Subject: [PATCH 043/742] Module Debugging: Canonicalize the file names used as PCH module names by stripping the path. Follow-up to r258555. This is safe because only one PCH per CU is currently supported for module debugging. rdar://problem/24301262 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258582 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit f5a6cecb8da7522684057bdf1029c01317029664) --- lib/CodeGen/ObjectFilePCHContainerOperations.cpp | 12 ++++++++---- lib/Serialization/ASTReader.cpp | 5 +++-- test/Modules/ExtDebugInfo.m | 2 +- test/Modules/ModuleDebugInfo.m | 2 +- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/lib/CodeGen/ObjectFilePCHContainerOperations.cpp b/lib/CodeGen/ObjectFilePCHContainerOperations.cpp index 406b433c95c..e3030a8e812 100644 --- a/lib/CodeGen/ObjectFilePCHContainerOperations.cpp +++ b/lib/CodeGen/ObjectFilePCHContainerOperations.cpp @@ -19,8 +19,8 @@ #include "clang/CodeGen/BackendUtil.h" #include "clang/Frontend/CodeGenOptions.h" #include "clang/Frontend/CompilerInstance.h" -#include "clang/Lex/Preprocessor.h" #include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/Preprocessor.h" #include "clang/Serialization/ASTWriter.h" #include "llvm/ADT/StringRef.h" #include "llvm/Bitcode/BitstreamReader.h" @@ -31,6 +31,7 @@ #include "llvm/IR/Module.h" #include "llvm/Object/COFF.h" #include "llvm/Object/ObjectFile.h" +#include "llvm/Support/Path.h" #include "llvm/Support/TargetRegistry.h" #include @@ -164,9 +165,12 @@ class PCHContainerGenerator : public ASTConsumer { M->setDataLayout(Ctx->getTargetInfo().getDataLayoutString()); Builder.reset(new CodeGen::CodeGenModule( *Ctx, HeaderSearchOpts, PreprocessorOpts, CodeGenOpts, *M, Diags)); - Builder->getModuleDebugInfo()->setModuleMap(MMap); - Builder->getModuleDebugInfo()->setPCHDescriptor( - {MainFileName, "", OutputFileName, ~1ULL}); + + // Prepare CGDebugInfo to emit debug info for a clang module. + auto *DI = Builder->getModuleDebugInfo(); + StringRef ModuleName = llvm::sys::path::filename(MainFileName); + DI->setPCHDescriptor({ModuleName, "", OutputFileName, ~1ULL}); + DI->setModuleMap(MMap); } bool HandleTopLevelDecl(DeclGroupRef D) override { diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 833ff57e4d0..97fbb45e121 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -7581,8 +7581,9 @@ ASTReader::getSourceDescriptor(unsigned ID) { // Chained PCH are not suported. if (ModuleMgr.size() == 1) { ModuleFile &MF = ModuleMgr.getPrimaryModule(); - return ASTReader::ASTSourceDescriptor( - MF.OriginalSourceFileName, MF.OriginalDir, MF.FileName, MF.Signature); + StringRef ModuleName = llvm::sys::path::filename(MF.OriginalSourceFileName); + return ASTReader::ASTSourceDescriptor(ModuleName, MF.OriginalDir, + MF.FileName, MF.Signature); } return None; } diff --git a/test/Modules/ExtDebugInfo.m b/test/Modules/ExtDebugInfo.m index 416f2813cec..5c3c7112b8c 100644 --- a/test/Modules/ExtDebugInfo.m +++ b/test/Modules/ExtDebugInfo.m @@ -34,7 +34,7 @@ int foo(ObjCClass *c) { // CHECK-SAME: scope: ![[MOD:[0-9]+]], // CHECK-SAME: flags: DIFlagFwdDecl) // CHECK-NOT: !DICompositeType(tag: DW_TAG_structure_type, -// CHECK: ![[MOD]] = !DIModule(scope: null, name: {{.*}}DebugObjC +// CHECK: ![[MOD]] = !DIModule(scope: null, name: "DebugObjC // CHECK: !DIGlobalVariable(name: "GlobalUnion", // CHECK-SAME: type: ![[GLOBAL_UNION:[0-9]+]] diff --git a/test/Modules/ModuleDebugInfo.m b/test/Modules/ModuleDebugInfo.m index 3d6c6657c91..6bca67656ae 100644 --- a/test/Modules/ModuleDebugInfo.m +++ b/test/Modules/ModuleDebugInfo.m @@ -28,7 +28,7 @@ // CHECK: !DICompositeType(tag: DW_TAG_enumeration_type, // CHECK-SAME: scope: ![[MODULE:[0-9]+]], -// CHECK: ![[MODULE]] = !DIModule(scope: null, name:{{.*}}DebugObjC +// CHECK: ![[MODULE]] = !DIModule(scope: null, name: "DebugObjC // CHECK: ![[TD_ENUM:.*]] = !DICompositeType(tag: DW_TAG_enumeration_type, // CHECK-SAME-NOT: name: From 6cdb0788d0a40d5453e607e95b6107fe8283f491 Mon Sep 17 00:00:00 2001 From: Anna Zaks Date: Sat, 23 Jan 2016 00:45:37 +0000 Subject: [PATCH 044/742] [analyzer] Fixup r258572 Utility to match function calls. Initialize the IdentifierInfo pointer. Hope this fixes the buildbot breakage. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258591 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit f293295dd52468dd70449edc0442b86912e18461) --- include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h index a67204c3bd4..55fd4b8880b 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -67,7 +67,7 @@ class CallDescription { /// call. Omit this parameter to match every occurance of call with a given /// name regardless the number of arguments. CallDescription(StringRef FuncName, unsigned RequiredArgs = NoArgRequirement) - : FuncName(FuncName), RequiredArgs(RequiredArgs) {} + : II(nullptr), FuncName(FuncName), RequiredArgs(RequiredArgs) {} }; template From 8a484d6b8427cc7a6bbe6607a919b873ba0cfba2 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Sat, 23 Jan 2016 01:09:07 +0000 Subject: [PATCH 045/742] [analyzer] SATestBuild.py: Remove html and log when producing reference results. The html reports are huge -- every issue in a given file results in a separate copy of the source code, in HTML form, for the file. This gets very large quickly and it doesn't make sense to check this into a reference repository. Also remove the log when generating reference results because it can leak absolute path names. We still keep both the html and the log around when producing non-reference results. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258594 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 1d03689e5357d66172c35b2ff550a0fa5d2a36d1) --- utils/analyzer/SATestBuild.py | 36 ++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/utils/analyzer/SATestBuild.py b/utils/analyzer/SATestBuild.py index 9c830933728..ab68518b5ac 100755 --- a/utils/analyzer/SATestBuild.py +++ b/utils/analyzer/SATestBuild.py @@ -397,19 +397,26 @@ def runAnalyzePreprocessed(Dir, SBOutputDir, Mode): if Failed == False: os.remove(LogFile.name); +def getBuildLogPath(SBOutputDir): + return os.path.join(SBOutputDir, LogFolderName, BuildLogName) + +def removeLogFile(SBOutputDir): + BuildLogPath = getBuildLogPath(SBOutputDir) + # Clean up the log file. + if (os.path.exists(BuildLogPath)) : + RmCommand = "rm '%s'" % BuildLogPath + if Verbose == 1: + print " Executing: %s" % (RmCommand,) + check_call(RmCommand, shell=True) + def buildProject(Dir, SBOutputDir, ProjectBuildMode, IsReferenceBuild): TBegin = time.time() - BuildLogPath = os.path.join(SBOutputDir, LogFolderName, BuildLogName) + BuildLogPath = getBuildLogPath(SBOutputDir) print "Log file: %s" % (BuildLogPath,) print "Output directory: %s" %(SBOutputDir, ) - # Clean up the log file. - if (os.path.exists(BuildLogPath)) : - RmCommand = "rm '%s'" % BuildLogPath - if Verbose == 1: - print " Executing: %s" % (RmCommand,) - check_call(RmCommand, shell=True) + removeLogFile(SBOutputDir) # Clean up scan build results. if (os.path.exists(SBOutputDir)) : @@ -585,6 +592,19 @@ def runCmpResults(Dir, Strictness = 0): print "Diagnostic comparison complete (time: %.2f)." % (time.time()-TBegin) return (NumDiffs > 0) +def cleanupReferenceResults(SBOutputDir): + # Delete html, css, and js files from reference results. These can + # include multiple copies of the benchmark source and so get very large. + Extensions = ["html", "css", "js"] + for E in Extensions: + for F in glob.glob("%s/*/*.%s" % (SBOutputDir, E)): + P = os.path.join(SBOutputDir, F) + RmCommand = "rm '%s'" % P + check_call(RmCommand, shell=True) + + # Remove the log file. It leaks absolute path names. + removeLogFile(SBOutputDir) + def updateSVN(Mode, ProjectsMap): try: ProjectsMap.seek(0) @@ -634,6 +654,8 @@ def testProject(ID, ProjectBuildMode, IsReferenceBuild=False, Dir=None, Strictne if IsReferenceBuild == False: runCmpResults(Dir, Strictness) + else: + cleanupReferenceResults(SBOutputDir) print "Completed tests for project %s (time: %.2f)." % \ (ID, (time.time()-TBegin)) From 35b3a680f81b12899b9f85d48e65f502b4473595 Mon Sep 17 00:00:00 2001 From: Alexey Bataev Date: Mon, 25 Jan 2016 05:14:03 +0000 Subject: [PATCH 046/742] Fixed processing of GNU extensions to C99 designated initializers Clang did not handles correctly inner parts of arrays/structures initializers in GNU extensions to C99 designated initializers. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258668 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 5687ebd4705286bed1fd99d47511a4fa1b39cdaa) --- lib/Sema/SemaInit.cpp | 12 ++++++------ test/CodeGen/init.c | 11 +++++++++++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index c3a89463dc6..c60ea865aa9 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -2276,7 +2276,7 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity, if (CheckDesignatedInitializer(MemberEntity, IList, DIE, DesigIdx + 1, FieldType, nullptr, nullptr, Index, StructuredList, newStructuredIndex, - true, false)) + FinishSubobjectInit, false)) return true; } @@ -2467,11 +2467,11 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity, Index = OldIndex; ElementEntity.setElementIndex(ElementIndex); - if (CheckDesignatedInitializer(ElementEntity, IList, DIE, DesigIdx + 1, - ElementType, nullptr, nullptr, Index, - StructuredList, ElementIndex, - (DesignatedStartIndex == DesignatedEndIndex), - false)) + if (CheckDesignatedInitializer( + ElementEntity, IList, DIE, DesigIdx + 1, ElementType, nullptr, + nullptr, Index, StructuredList, ElementIndex, + FinishSubobjectInit && (DesignatedStartIndex == DesignatedEndIndex), + false)) return true; // Move to the next index in the array that we'll be initializing. diff --git a/test/CodeGen/init.c b/test/CodeGen/init.c index a2b492013d4..5d086723cc0 100644 --- a/test/CodeGen/init.c +++ b/test/CodeGen/init.c @@ -1,5 +1,16 @@ // RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm %s -o - | FileCheck %s +struct I { int k[3]; }; +struct M { struct I o[2]; }; +struct M v1[1] = { [0].o[0 ... 1].k[0 ... 1] = 4, 5 }; +unsigned v2[2][3] = {[0 ... 1][0 ... 1] = 2222, 3333}; + +// CHECK-DAG: %struct.M = type { [2 x %struct.I] } +// CHECK-DAG: %struct.I = type { [3 x i32] } + +// CHECK: [1 x %struct.M] [%struct.M { [2 x %struct.I] [%struct.I { [3 x i32] [i32 4, i32 4, i32 0] }, %struct.I { [3 x i32] [i32 4, i32 4, i32 5] }] }], +// CHECK: [2 x [3 x i32]] {{[[][[]}}3 x i32] [i32 2222, i32 2222, i32 0], [3 x i32] [i32 2222, i32 2222, i32 3333]], + void f1() { // Scalars in braces. int a = { 1 }; From 74acbdd49db729b97c535247c73fc6a2b689b5c5 Mon Sep 17 00:00:00 2001 From: Alexey Bataev Date: Mon, 25 Jan 2016 07:06:23 +0000 Subject: [PATCH 047/742] Allow capture typedefs/type aliases for VLAs in lambdas/captured statements chain. Previous it was allowed to capture VLAs/types with arrays of runtime bounds only inside the first lambda/capture statement in stack. Patch allows to capture these typedefs implicitly in chains of lambdas/captured statements. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258669 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit c31c6a6f2f51d0fadca8a87c9bcff050e4e7b6c7) --- lib/Sema/SemaExpr.cpp | 18 ++++++++++++++---- test/CodeGenCXX/lambda-expressions.cpp | 10 +++++++--- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 5ba95a2fbda..c6ebce8c1af 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -3887,14 +3887,24 @@ Sema::CreateUnaryExprOrTypeTraitExpr(TypeSourceInfo *TInfo, if (T->isVariablyModifiedType() && FunctionScopes.size() > 1) { if (auto *TT = T->getAs()) { - if (auto *CSI = dyn_cast(FunctionScopes.back())) { + for (auto I = FunctionScopes.rbegin(), + E = std::prev(FunctionScopes.rend()); + I != E; ++I) { + auto *CSI = dyn_cast(*I); + if (CSI == nullptr) + break; DeclContext *DC = nullptr; - if (auto LSI = dyn_cast(CSI)) + if (auto *LSI = dyn_cast(CSI)) DC = LSI->CallOperator; - else if (auto CRSI = dyn_cast(CSI)) + else if (auto *CRSI = dyn_cast(CSI)) DC = CRSI->TheCapturedDecl; - if (DC && TT->getDecl()->getDeclContext() != DC) + else if (auto *BSI = dyn_cast(CSI)) + DC = BSI->TheDecl; + if (DC) { + if (DC->containsDecl(TT->getDecl())) + break; captureVariablyModifiedType(Context, T, CSI); + } } } } diff --git a/test/CodeGenCXX/lambda-expressions.cpp b/test/CodeGenCXX/lambda-expressions.cpp index 4df44f4c5f7..f59d360314e 100644 --- a/test/CodeGenCXX/lambda-expressions.cpp +++ b/test/CodeGenCXX/lambda-expressions.cpp @@ -12,12 +12,16 @@ extern "C" auto cvar = []{}; // CHECK-LABEL: define i32 @_Z9ARBSizeOfi(i32 int ARBSizeOf(int n) { - typedef double (T)[8][n]; - using TT = double [8][n]; + typedef double(T)[8][n]; + using TT = double[8][n]; return [&]() -> int { typedef double(T1)[8][n]; using TT1 = double[8][n]; - return sizeof(T) + sizeof(T1) + sizeof(TT) + sizeof(TT1); + return [&n]() -> int { + typedef double(T2)[8][n]; + using TT2 = double[8][n]; + return sizeof(T) + sizeof(T1) + sizeof(T2) + sizeof(TT) + sizeof(TT1) + sizeof(TT2); + }(); }(); } From c124d69fcb2f2d22f8274336bda49d2fa4fc3c00 Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Mon, 25 Jan 2016 21:52:26 +0000 Subject: [PATCH 048/742] Move ObjCPropertyDecl to before ObjCContainerDecl. After we add ObjCPropertyDecl::isClassProperty, we can use it in ObjCContainerDecl to define filter to iterate over instance properties and class properties. This is the first patch in a series of patches to support class properties in addition to instance properties in objective-c. rdar://23891898 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258727 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 4f6e709cff631349dcb83f2f80cca60dd964bfbf) --- include/clang/AST/DeclObjC.h | 382 +++++++++++++++++------------------ 1 file changed, 191 insertions(+), 191 deletions(-) diff --git a/include/clang/AST/DeclObjC.h b/include/clang/AST/DeclObjC.h index f46078f28a7..7de303c41a0 100644 --- a/include/clang/AST/DeclObjC.h +++ b/include/clang/AST/DeclObjC.h @@ -689,6 +689,197 @@ class ObjCTypeParamList final friend TrailingObjects; }; +/// \brief Represents one property declaration in an Objective-C interface. +/// +/// For example: +/// \code{.mm} +/// \@property (assign, readwrite) int MyProperty; +/// \endcode +class ObjCPropertyDecl : public NamedDecl { + void anchor() override; +public: + enum PropertyAttributeKind { + OBJC_PR_noattr = 0x00, + OBJC_PR_readonly = 0x01, + OBJC_PR_getter = 0x02, + OBJC_PR_assign = 0x04, + OBJC_PR_readwrite = 0x08, + OBJC_PR_retain = 0x10, + OBJC_PR_copy = 0x20, + OBJC_PR_nonatomic = 0x40, + OBJC_PR_setter = 0x80, + OBJC_PR_atomic = 0x100, + OBJC_PR_weak = 0x200, + OBJC_PR_strong = 0x400, + OBJC_PR_unsafe_unretained = 0x800, + /// Indicates that the nullability of the type was spelled with a + /// property attribute rather than a type qualifier. + OBJC_PR_nullability = 0x1000, + OBJC_PR_null_resettable = 0x2000, + // Adding a property should change NumPropertyAttrsBits + }; + + enum { + /// \brief Number of bits fitting all the property attributes. + NumPropertyAttrsBits = 14 + }; + + enum SetterKind { Assign, Retain, Copy, Weak }; + enum PropertyControl { None, Required, Optional }; +private: + SourceLocation AtLoc; // location of \@property + SourceLocation LParenLoc; // location of '(' starting attribute list or null. + QualType DeclType; + TypeSourceInfo *DeclTypeSourceInfo; + unsigned PropertyAttributes : NumPropertyAttrsBits; + unsigned PropertyAttributesAsWritten : NumPropertyAttrsBits; + // \@required/\@optional + unsigned PropertyImplementation : 2; + + Selector GetterName; // getter name of NULL if no getter + Selector SetterName; // setter name of NULL if no setter + + ObjCMethodDecl *GetterMethodDecl; // Declaration of getter instance method + ObjCMethodDecl *SetterMethodDecl; // Declaration of setter instance method + ObjCIvarDecl *PropertyIvarDecl; // Synthesize ivar for this property + + ObjCPropertyDecl(DeclContext *DC, SourceLocation L, IdentifierInfo *Id, + SourceLocation AtLocation, SourceLocation LParenLocation, + QualType T, TypeSourceInfo *TSI, + PropertyControl propControl) + : NamedDecl(ObjCProperty, DC, L, Id), AtLoc(AtLocation), + LParenLoc(LParenLocation), DeclType(T), DeclTypeSourceInfo(TSI), + PropertyAttributes(OBJC_PR_noattr), + PropertyAttributesAsWritten(OBJC_PR_noattr), + PropertyImplementation(propControl), + GetterName(Selector()), + SetterName(Selector()), + GetterMethodDecl(nullptr), SetterMethodDecl(nullptr), + PropertyIvarDecl(nullptr) {} + +public: + static ObjCPropertyDecl *Create(ASTContext &C, DeclContext *DC, + SourceLocation L, + IdentifierInfo *Id, SourceLocation AtLocation, + SourceLocation LParenLocation, + QualType T, + TypeSourceInfo *TSI, + PropertyControl propControl = None); + + static ObjCPropertyDecl *CreateDeserialized(ASTContext &C, unsigned ID); + + SourceLocation getAtLoc() const { return AtLoc; } + void setAtLoc(SourceLocation L) { AtLoc = L; } + + SourceLocation getLParenLoc() const { return LParenLoc; } + void setLParenLoc(SourceLocation L) { LParenLoc = L; } + + TypeSourceInfo *getTypeSourceInfo() const { return DeclTypeSourceInfo; } + + QualType getType() const { return DeclType; } + + void setType(QualType T, TypeSourceInfo *TSI) { + DeclType = T; + DeclTypeSourceInfo = TSI; + } + + /// Retrieve the type when this property is used with a specific base object + /// type. + QualType getUsageType(QualType objectType) const; + + PropertyAttributeKind getPropertyAttributes() const { + return PropertyAttributeKind(PropertyAttributes); + } + void setPropertyAttributes(PropertyAttributeKind PRVal) { + PropertyAttributes |= PRVal; + } + void overwritePropertyAttributes(unsigned PRVal) { + PropertyAttributes = PRVal; + } + + PropertyAttributeKind getPropertyAttributesAsWritten() const { + return PropertyAttributeKind(PropertyAttributesAsWritten); + } + + void setPropertyAttributesAsWritten(PropertyAttributeKind PRVal) { + PropertyAttributesAsWritten = PRVal; + } + + // Helper methods for accessing attributes. + + /// isReadOnly - Return true iff the property has a setter. + bool isReadOnly() const { + return (PropertyAttributes & OBJC_PR_readonly); + } + + /// isAtomic - Return true if the property is atomic. + bool isAtomic() const { + return (PropertyAttributes & OBJC_PR_atomic); + } + + /// isRetaining - Return true if the property retains its value. + bool isRetaining() const { + return (PropertyAttributes & + (OBJC_PR_retain | OBJC_PR_strong | OBJC_PR_copy)); + } + + /// getSetterKind - Return the method used for doing assignment in + /// the property setter. This is only valid if the property has been + /// defined to have a setter. + SetterKind getSetterKind() const { + if (PropertyAttributes & OBJC_PR_strong) + return getType()->isBlockPointerType() ? Copy : Retain; + if (PropertyAttributes & OBJC_PR_retain) + return Retain; + if (PropertyAttributes & OBJC_PR_copy) + return Copy; + if (PropertyAttributes & OBJC_PR_weak) + return Weak; + return Assign; + } + + Selector getGetterName() const { return GetterName; } + void setGetterName(Selector Sel) { GetterName = Sel; } + + Selector getSetterName() const { return SetterName; } + void setSetterName(Selector Sel) { SetterName = Sel; } + + ObjCMethodDecl *getGetterMethodDecl() const { return GetterMethodDecl; } + void setGetterMethodDecl(ObjCMethodDecl *gDecl) { GetterMethodDecl = gDecl; } + + ObjCMethodDecl *getSetterMethodDecl() const { return SetterMethodDecl; } + void setSetterMethodDecl(ObjCMethodDecl *gDecl) { SetterMethodDecl = gDecl; } + + // Related to \@optional/\@required declared in \@protocol + void setPropertyImplementation(PropertyControl pc) { + PropertyImplementation = pc; + } + PropertyControl getPropertyImplementation() const { + return PropertyControl(PropertyImplementation); + } + + void setPropertyIvarDecl(ObjCIvarDecl *Ivar) { + PropertyIvarDecl = Ivar; + } + ObjCIvarDecl *getPropertyIvarDecl() const { + return PropertyIvarDecl; + } + + SourceRange getSourceRange() const override LLVM_READONLY { + return SourceRange(AtLoc, getLocation()); + } + + /// Get the default name of the synthesized ivar. + IdentifierInfo *getDefaultSynthIvarName(ASTContext &Ctx) const; + + /// Lookup a property by name in the specified DeclContext. + static ObjCPropertyDecl *findPropertyDecl(const DeclContext *DC, + const IdentifierInfo *propertyID); + + static bool classof(const Decl *D) { return classofKind(D->getKind()); } + static bool classofKind(Kind K) { return K == ObjCProperty; } +}; + /// ObjCContainerDecl - Represents a container for method declarations. /// Current sub-classes are ObjCInterfaceDecl, ObjCCategoryDecl, /// ObjCProtocolDecl, and ObjCImplDecl. @@ -2407,197 +2598,6 @@ class ObjCCompatibleAliasDecl : public NamedDecl { }; -/// \brief Represents one property declaration in an Objective-C interface. -/// -/// For example: -/// \code{.mm} -/// \@property (assign, readwrite) int MyProperty; -/// \endcode -class ObjCPropertyDecl : public NamedDecl { - void anchor() override; -public: - enum PropertyAttributeKind { - OBJC_PR_noattr = 0x00, - OBJC_PR_readonly = 0x01, - OBJC_PR_getter = 0x02, - OBJC_PR_assign = 0x04, - OBJC_PR_readwrite = 0x08, - OBJC_PR_retain = 0x10, - OBJC_PR_copy = 0x20, - OBJC_PR_nonatomic = 0x40, - OBJC_PR_setter = 0x80, - OBJC_PR_atomic = 0x100, - OBJC_PR_weak = 0x200, - OBJC_PR_strong = 0x400, - OBJC_PR_unsafe_unretained = 0x800, - /// Indicates that the nullability of the type was spelled with a - /// property attribute rather than a type qualifier. - OBJC_PR_nullability = 0x1000, - OBJC_PR_null_resettable = 0x2000 - // Adding a property should change NumPropertyAttrsBits - }; - - enum { - /// \brief Number of bits fitting all the property attributes. - NumPropertyAttrsBits = 14 - }; - - enum SetterKind { Assign, Retain, Copy, Weak }; - enum PropertyControl { None, Required, Optional }; -private: - SourceLocation AtLoc; // location of \@property - SourceLocation LParenLoc; // location of '(' starting attribute list or null. - QualType DeclType; - TypeSourceInfo *DeclTypeSourceInfo; - unsigned PropertyAttributes : NumPropertyAttrsBits; - unsigned PropertyAttributesAsWritten : NumPropertyAttrsBits; - // \@required/\@optional - unsigned PropertyImplementation : 2; - - Selector GetterName; // getter name of NULL if no getter - Selector SetterName; // setter name of NULL if no setter - - ObjCMethodDecl *GetterMethodDecl; // Declaration of getter instance method - ObjCMethodDecl *SetterMethodDecl; // Declaration of setter instance method - ObjCIvarDecl *PropertyIvarDecl; // Synthesize ivar for this property - - ObjCPropertyDecl(DeclContext *DC, SourceLocation L, IdentifierInfo *Id, - SourceLocation AtLocation, SourceLocation LParenLocation, - QualType T, TypeSourceInfo *TSI, - PropertyControl propControl) - : NamedDecl(ObjCProperty, DC, L, Id), AtLoc(AtLocation), - LParenLoc(LParenLocation), DeclType(T), DeclTypeSourceInfo(TSI), - PropertyAttributes(OBJC_PR_noattr), - PropertyAttributesAsWritten(OBJC_PR_noattr), - PropertyImplementation(propControl), - GetterName(Selector()), - SetterName(Selector()), - GetterMethodDecl(nullptr), SetterMethodDecl(nullptr), - PropertyIvarDecl(nullptr) {} - -public: - static ObjCPropertyDecl *Create(ASTContext &C, DeclContext *DC, - SourceLocation L, - IdentifierInfo *Id, SourceLocation AtLocation, - SourceLocation LParenLocation, - QualType T, - TypeSourceInfo *TSI, - PropertyControl propControl = None); - - static ObjCPropertyDecl *CreateDeserialized(ASTContext &C, unsigned ID); - - SourceLocation getAtLoc() const { return AtLoc; } - void setAtLoc(SourceLocation L) { AtLoc = L; } - - SourceLocation getLParenLoc() const { return LParenLoc; } - void setLParenLoc(SourceLocation L) { LParenLoc = L; } - - TypeSourceInfo *getTypeSourceInfo() const { return DeclTypeSourceInfo; } - - QualType getType() const { return DeclType; } - - void setType(QualType T, TypeSourceInfo *TSI) { - DeclType = T; - DeclTypeSourceInfo = TSI; - } - - /// Retrieve the type when this property is used with a specific base object - /// type. - QualType getUsageType(QualType objectType) const; - - PropertyAttributeKind getPropertyAttributes() const { - return PropertyAttributeKind(PropertyAttributes); - } - void setPropertyAttributes(PropertyAttributeKind PRVal) { - PropertyAttributes |= PRVal; - } - void overwritePropertyAttributes(unsigned PRVal) { - PropertyAttributes = PRVal; - } - - PropertyAttributeKind getPropertyAttributesAsWritten() const { - return PropertyAttributeKind(PropertyAttributesAsWritten); - } - - void setPropertyAttributesAsWritten(PropertyAttributeKind PRVal) { - PropertyAttributesAsWritten = PRVal; - } - - // Helper methods for accessing attributes. - - /// isReadOnly - Return true iff the property has a setter. - bool isReadOnly() const { - return (PropertyAttributes & OBJC_PR_readonly); - } - - /// isAtomic - Return true if the property is atomic. - bool isAtomic() const { - return (PropertyAttributes & OBJC_PR_atomic); - } - - /// isRetaining - Return true if the property retains its value. - bool isRetaining() const { - return (PropertyAttributes & - (OBJC_PR_retain | OBJC_PR_strong | OBJC_PR_copy)); - } - - /// getSetterKind - Return the method used for doing assignment in - /// the property setter. This is only valid if the property has been - /// defined to have a setter. - SetterKind getSetterKind() const { - if (PropertyAttributes & OBJC_PR_strong) - return getType()->isBlockPointerType() ? Copy : Retain; - if (PropertyAttributes & OBJC_PR_retain) - return Retain; - if (PropertyAttributes & OBJC_PR_copy) - return Copy; - if (PropertyAttributes & OBJC_PR_weak) - return Weak; - return Assign; - } - - Selector getGetterName() const { return GetterName; } - void setGetterName(Selector Sel) { GetterName = Sel; } - - Selector getSetterName() const { return SetterName; } - void setSetterName(Selector Sel) { SetterName = Sel; } - - ObjCMethodDecl *getGetterMethodDecl() const { return GetterMethodDecl; } - void setGetterMethodDecl(ObjCMethodDecl *gDecl) { GetterMethodDecl = gDecl; } - - ObjCMethodDecl *getSetterMethodDecl() const { return SetterMethodDecl; } - void setSetterMethodDecl(ObjCMethodDecl *gDecl) { SetterMethodDecl = gDecl; } - - // Related to \@optional/\@required declared in \@protocol - void setPropertyImplementation(PropertyControl pc) { - PropertyImplementation = pc; - } - PropertyControl getPropertyImplementation() const { - return PropertyControl(PropertyImplementation); - } - - void setPropertyIvarDecl(ObjCIvarDecl *Ivar) { - PropertyIvarDecl = Ivar; - } - ObjCIvarDecl *getPropertyIvarDecl() const { - return PropertyIvarDecl; - } - - SourceRange getSourceRange() const override LLVM_READONLY { - return SourceRange(AtLoc, getLocation()); - } - - /// Get the default name of the synthesized ivar. - IdentifierInfo *getDefaultSynthIvarName(ASTContext &Ctx) const; - - /// Lookup a property by name in the specified DeclContext. - static ObjCPropertyDecl *findPropertyDecl(const DeclContext *DC, - const IdentifierInfo *propertyID); - - static bool classof(const Decl *D) { return classofKind(D->getKind()); } - static bool classofKind(Kind K) { return K == ObjCProperty; } -}; - /// ObjCPropertyImplDecl - Represents implementation declaration of a property /// in a class or category implementation block. For example: /// \@synthesize prop1 = ivar1; From efe9a9f4ade15cc820adc4bace715f0160af0353 Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Mon, 25 Jan 2016 22:37:47 +0000 Subject: [PATCH 049/742] Update comments to match the implementation. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258735 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit d8c3cb278463049a0f0c6c9fb7031e3e0a18ad21) --- lib/CodeGen/CGObjCMac.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index 4ad3b75e328..6def0134683 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/lib/CodeGen/CGObjCMac.cpp @@ -5361,6 +5361,7 @@ ObjCTypesHelper::ObjCTypesHelper(CodeGen::CodeGenModule &cgm) // char *class_name; // struct _objc_method_list *instance_method; // struct _objc_method_list *class_method; + // struct _objc_protocol_list *protocols; // uint32_t size; // sizeof(struct _objc_category) // struct _objc_property_list *instance_properties;// category's @property // } From 628cbc3422eb6b5fb990c73a058802786a01135b Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Tue, 26 Jan 2016 18:05:23 +0000 Subject: [PATCH 050/742] Use instance_properties instead of properties. NFC. All current properties are instance properties. This is the second patch in a series of patches to support class properties in addition to instance properties in objective-c. rdar://23891898 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258824 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 00fef86c90e0dae2bac1905d5317fad36e5b591c) --- include/clang/AST/DeclObjC.h | 8 +++++--- lib/ARCMigrate/ObjCMT.cpp | 4 ++-- lib/ARCMigrate/TransProperties.cpp | 2 +- lib/AST/DeclObjC.cpp | 12 ++++++------ lib/CodeGen/CGDebugInfo.cpp | 4 ++-- lib/CodeGen/CGObjCGNU.cpp | 2 +- lib/CodeGen/CGObjCMac.cpp | 6 +++--- lib/Frontend/Rewrite/RewriteModernObjC.cpp | 15 +++++++++------ lib/Frontend/Rewrite/RewriteObjC.cpp | 6 +++--- lib/Sema/SemaCodeComplete.cpp | 4 ++-- lib/Sema/SemaDeclObjC.cpp | 4 ++-- lib/Sema/SemaObjCProperty.cpp | 16 ++++++++-------- .../Checkers/DirectIvarAssignment.cpp | 2 +- 13 files changed, 45 insertions(+), 40 deletions(-) diff --git a/include/clang/AST/DeclObjC.h b/include/clang/AST/DeclObjC.h index 7de303c41a0..c5349d59c48 100644 --- a/include/clang/AST/DeclObjC.h +++ b/include/clang/AST/DeclObjC.h @@ -904,11 +904,13 @@ class ObjCContainerDecl : public NamedDecl, public DeclContext { typedef llvm::iterator_range> prop_range; - prop_range properties() const { return prop_range(prop_begin(), prop_end()); } - prop_iterator prop_begin() const { + prop_range instance_properties() const { + return prop_range(instprop_begin(), instprop_end()); + } + prop_iterator instprop_begin() const { return prop_iterator(decls_begin()); } - prop_iterator prop_end() const { + prop_iterator instprop_end() const { return prop_iterator(decls_end()); } diff --git a/lib/ARCMigrate/ObjCMT.cpp b/lib/ARCMigrate/ObjCMT.cpp index 50b113660d3..10996e83208 100644 --- a/lib/ARCMigrate/ObjCMT.cpp +++ b/lib/ARCMigrate/ObjCMT.cpp @@ -588,7 +588,7 @@ void ObjCMigrateASTConsumer::migrateObjCContainerDecl(ASTContext &Ctx, if (!(ASTMigrateActions & FrontendOptions::ObjCMT_ReturnsInnerPointerProperty)) return; - for (auto *Prop : D->properties()) { + for (auto *Prop : D->instance_properties()) { if ((ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) && !Prop->isDeprecated()) migratePropertyNsReturnsInnerPointer(Ctx, Prop); @@ -605,7 +605,7 @@ ClassImplementsAllMethodsAndProperties(ASTContext &Ctx, // in class interface. bool HasAtleastOneRequiredProperty = false; if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition()) - for (const auto *Property : PDecl->properties()) { + for (const auto *Property : PDecl->instance_properties()) { if (Property->getPropertyImplementation() == ObjCPropertyDecl::Optional) continue; HasAtleastOneRequiredProperty = true; diff --git a/lib/ARCMigrate/TransProperties.cpp b/lib/ARCMigrate/TransProperties.cpp index 8667bc2a37d..389b03666bf 100644 --- a/lib/ARCMigrate/TransProperties.cpp +++ b/lib/ARCMigrate/TransProperties.cpp @@ -76,7 +76,7 @@ class PropertiesRewriter { static void collectProperties(ObjCContainerDecl *D, AtPropDeclsTy &AtProps, AtPropDeclsTy *PrevAtProps = nullptr) { - for (auto *Prop : D->properties()) { + for (auto *Prop : D->instance_properties()) { if (Prop->getAtLoc().isInvalid()) continue; unsigned RawLoc = Prop->getAtLoc().getRawEncoding(); diff --git a/lib/AST/DeclObjC.cpp b/lib/AST/DeclObjC.cpp index 050a0f53f1e..aa012edf65c 100644 --- a/lib/AST/DeclObjC.cpp +++ b/lib/AST/DeclObjC.cpp @@ -122,7 +122,7 @@ bool ObjCContainerDecl::HasUserDeclaredSetterMethod( // declaration of this property. If one found, presumably a setter will // be provided (properties declared in categories will not get // auto-synthesized). - for (const auto *P : Cat->properties()) + for (const auto *P : Cat->instance_properties()) if (P->getIdentifier() == Property->getIdentifier()) { if (P->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_readwrite) return true; @@ -341,13 +341,13 @@ ObjCInterfaceDecl::FindPropertyVisibleInPrimaryClass( void ObjCInterfaceDecl::collectPropertiesToImplement(PropertyMap &PM, PropertyDeclOrder &PO) const { - for (auto *Prop : properties()) { + for (auto *Prop : instance_properties()) { PM[Prop->getIdentifier()] = Prop; PO.push_back(Prop); } for (const auto *Ext : known_extensions()) { const ObjCCategoryDecl *ClassExt = Ext; - for (auto *Prop : ClassExt->properties()) { + for (auto *Prop : ClassExt->instance_properties()) { PM[Prop->getIdentifier()] = Prop; PO.push_back(Prop); } @@ -1218,7 +1218,7 @@ ObjCMethodDecl::findPropertyDecl(bool CheckOverrides) const { auto findMatchingProperty = [&](const ObjCContainerDecl *Container) -> const ObjCPropertyDecl * { - for (const auto *I : Container->properties()) { + for (const auto *I : Container->instance_properties()) { Selector NextSel = IsGetter ? I->getGetterName() : I->getSetterName(); if (NextSel == Sel) @@ -1820,7 +1820,7 @@ void ObjCProtocolDecl::collectPropertiesToImplement(PropertyMap &PM, PropertyDeclOrder &PO) const { if (const ObjCProtocolDecl *PDecl = getDefinition()) { - for (auto *Prop : PDecl->properties()) { + for (auto *Prop : PDecl->instance_properties()) { // Insert into PM if not there already. PM.insert(std::make_pair(Prop->getIdentifier(), Prop)); PO.push_back(Prop); @@ -1837,7 +1837,7 @@ void ObjCProtocolDecl::collectInheritedProtocolProperties( ProtocolPropertyMap &PM) const { if (const ObjCProtocolDecl *PDecl = getDefinition()) { bool MatchFound = false; - for (auto *Prop : PDecl->properties()) { + for (auto *Prop : PDecl->instance_properties()) { if (Prop == Property) continue; if (Prop->getIdentifier() == Property->getIdentifier()) { diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index 675b5eb07c5..6d19e50e9f7 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -1822,11 +1822,11 @@ llvm::DIType *CGDebugInfo::CreateTypeDefinition(const ObjCInterfaceType *Ty, { llvm::SmallPtrSet PropertySet; for (const ObjCCategoryDecl *ClassExt : ID->known_extensions()) - for (auto *PD : ClassExt->properties()) { + for (auto *PD : ClassExt->instance_properties()) { PropertySet.insert(PD->getIdentifier()); AddProperty(PD); } - for (const auto *PD : ID->properties()) { + for (const auto *PD : ID->instance_properties()) { // Don't emit duplicate metadata for properties that were already in a // class extension. if (!PropertySet.insert(PD->getIdentifier()).second) diff --git a/lib/CodeGen/CGObjCGNU.cpp b/lib/CodeGen/CGObjCGNU.cpp index a2ec4e13e8d..e4074a7482d 100644 --- a/lib/CodeGen/CGObjCGNU.cpp +++ b/lib/CodeGen/CGObjCGNU.cpp @@ -1850,7 +1850,7 @@ void CGObjCGNU::GenerateProtocol(const ObjCProtocolDecl *PD) { // Add all of the property methods need adding to the method list and to the // property metadata list. - for (auto *property : PD->properties()) { + for (auto *property : PD->instance_properties()) { std::vector Fields; Fields.push_back(MakePropertyEncodingString(property, nullptr)); diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index 6def0134683..f847fa1b7fa 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/lib/CodeGen/CGObjCMac.cpp @@ -2883,7 +2883,7 @@ PushProtocolProperties(llvm::SmallPtrSet &PropertySet, const ObjCCommonTypesHelper &ObjCTypes) { for (const auto *P : Proto->protocols()) PushProtocolProperties(PropertySet, Properties, Container, P, ObjCTypes); - for (const auto *PD : Proto->properties()) { + for (const auto *PD : Proto->instance_properties()) { if (!PropertySet.insert(PD->getIdentifier()).second) continue; llvm::Constant *Prop[] = { @@ -2920,11 +2920,11 @@ llvm::Constant *CGObjCCommonMac::EmitPropertyList(Twine Name, }; if (const ObjCInterfaceDecl *OID = dyn_cast(OCD)) for (const ObjCCategoryDecl *ClassExt : OID->known_extensions()) - for (auto *PD : ClassExt->properties()) { + for (auto *PD : ClassExt->instance_properties()) { PropertySet.insert(PD->getIdentifier()); AddProperty(PD); } - for (const auto *PD : OCD->properties()) { + for (const auto *PD : OCD->instance_properties()) { // Don't emit duplicate metadata for properties that were already in a // class extension. if (!PropertySet.insert(PD->getIdentifier()).second) diff --git a/lib/Frontend/Rewrite/RewriteModernObjC.cpp b/lib/Frontend/Rewrite/RewriteModernObjC.cpp index be68d42affa..9fae7f02995 100644 --- a/lib/Frontend/Rewrite/RewriteModernObjC.cpp +++ b/lib/Frontend/Rewrite/RewriteModernObjC.cpp @@ -1147,7 +1147,7 @@ void RewriteModernObjC::RewriteCategoryDecl(ObjCCategoryDecl *CatDecl) { ReplaceText(LocStart, 0, "// "); } - for (auto *I : CatDecl->properties()) + for (auto *I : CatDecl->instance_properties()) RewriteProperty(I); for (auto *I : CatDecl->instance_methods()) @@ -1171,7 +1171,7 @@ void RewriteModernObjC::RewriteProtocolDecl(ObjCProtocolDecl *PDecl) { RewriteMethodDeclaration(I); for (auto *I : PDecl->class_methods()) RewriteMethodDeclaration(I); - for (auto *I : PDecl->properties()) + for (auto *I : PDecl->instance_properties()) RewriteProperty(I); // Lastly, comment out the @end. @@ -1417,7 +1417,7 @@ void RewriteModernObjC::RewriteInterfaceDecl(ObjCInterfaceDecl *ClassDecl) { // Mark this typedef as having been written into its c++ equivalent. ObjCWrittenInterfaces.insert(ClassDecl->getCanonicalDecl()); - for (auto *I : ClassDecl->properties()) + for (auto *I : ClassDecl->instance_properties()) RewriteProperty(I); for (auto *I : ClassDecl->instance_methods()) RewriteMethodDeclaration(I); @@ -6995,7 +6995,8 @@ void RewriteModernObjC::RewriteObjCProtocolMetaData(ObjCProtocolDecl *PDecl, PDecl->getNameAsString(), false); // Protocol's property metadata. - SmallVector ProtocolProperties(PDecl->properties()); + SmallVector ProtocolProperties( + PDecl->instance_properties()); Write_prop_list_t_initializer(*this, Context, Result, ProtocolProperties, /* Container */nullptr, "_OBJC_PROTOCOL_PROPERTIES_", @@ -7208,7 +7209,8 @@ void RewriteModernObjC::RewriteObjCClassMetaData(ObjCImplementationDecl *IDecl, IDecl->getNameAsString()); // Protocol's property metadata. - SmallVector ClassProperties(CDecl->properties()); + SmallVector ClassProperties( + CDecl->instance_properties()); Write_prop_list_t_initializer(*this, Context, Result, ClassProperties, /* Container */IDecl, "_OBJC_$_PROP_LIST_", @@ -7453,7 +7455,8 @@ void RewriteModernObjC::RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *IDecl, FullCategoryName); // Protocol's property metadata. - SmallVector ClassProperties(CDecl->properties()); + SmallVector ClassProperties( + CDecl->instance_properties()); Write_prop_list_t_initializer(*this, Context, Result, ClassProperties, /* Container */IDecl, "_OBJC_$_PROP_LIST_", diff --git a/lib/Frontend/Rewrite/RewriteObjC.cpp b/lib/Frontend/Rewrite/RewriteObjC.cpp index e0ddadb1230..67b2bdef5de 100644 --- a/lib/Frontend/Rewrite/RewriteObjC.cpp +++ b/lib/Frontend/Rewrite/RewriteObjC.cpp @@ -969,7 +969,7 @@ void RewriteObjC::RewriteCategoryDecl(ObjCCategoryDecl *CatDecl) { // FIXME: handle category headers that are declared across multiple lines. ReplaceText(LocStart, 0, "// "); - for (auto *I : CatDecl->properties()) + for (auto *I : CatDecl->instance_properties()) RewriteProperty(I); for (auto *I : CatDecl->instance_methods()) RewriteMethodDeclaration(I); @@ -992,7 +992,7 @@ void RewriteObjC::RewriteProtocolDecl(ObjCProtocolDecl *PDecl) { RewriteMethodDeclaration(I); for (auto *I : PDecl->class_methods()) RewriteMethodDeclaration(I); - for (auto *I : PDecl->properties()) + for (auto *I : PDecl->instance_properties()) RewriteProperty(I); // Lastly, comment out the @end. @@ -1210,7 +1210,7 @@ void RewriteObjC::RewriteInterfaceDecl(ObjCInterfaceDecl *ClassDecl) { } RewriteObjCInternalStruct(ClassDecl, ResultStr); - for (auto *I : ClassDecl->properties()) + for (auto *I : ClassDecl->instance_properties()) RewriteProperty(I); for (auto *I : ClassDecl->instance_methods()) RewriteMethodDeclaration(I); diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index 21cf6258514..59ab0ba618d 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -3570,7 +3570,7 @@ static void AddObjCProperties(const CodeCompletionContext &CCContext, Container = getContainerDef(Container); // Add properties in this container. - for (const auto *P : Container->properties()) + for (const auto *P : Container->instance_properties()) if (AddedProperties.insert(P->getIdentifier()).second) Results.MaybeAddResult(Result(P, Results.getBasePriority(P), nullptr), CurContext); @@ -7178,7 +7178,7 @@ void Sema::CodeCompleteObjCMethodDecl(Scope *S, Containers.push_back(Cat); for (unsigned I = 0, N = Containers.size(); I != N; ++I) - for (auto *P : Containers[I]->properties()) + for (auto *P : Containers[I]->instance_properties()) AddObjCKeyValueCompletions(P, IsInstanceMethod, ReturnType, Context, KnownSelectors, Results); } diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index 0aeec4fc2cd..ece34f26799 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -3641,7 +3641,7 @@ Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, ArrayRef allMethods, // ProcessPropertyDecl is responsible for diagnosing conflicts with any // user-defined setter/getter. It also synthesizes setter/getter methods // and adds them to the DeclContext and global method pools. - for (auto *I : CDecl->properties()) + for (auto *I : CDecl->instance_properties()) ProcessPropertyDecl(I); CDecl->setAtEndRange(AtEnd); } @@ -3654,7 +3654,7 @@ Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, ArrayRef allMethods, // property will be synthesized when property with same name is // seen in the @implementation. for (const auto *Ext : IDecl->visible_extensions()) { - for (const auto *Property : Ext->properties()) { + for (const auto *Property : Ext->instance_properties()) { // Skip over properties declared @dynamic if (const ObjCPropertyImplDecl *PIDecl = IC->FindPropertyImplDecl(Property->getIdentifier())) diff --git a/lib/Sema/SemaObjCProperty.cpp b/lib/Sema/SemaObjCProperty.cpp index 1cb84e44806..db37a754791 100644 --- a/lib/Sema/SemaObjCProperty.cpp +++ b/lib/Sema/SemaObjCProperty.cpp @@ -1521,7 +1521,7 @@ static void CollectImmediateProperties(ObjCContainerDecl *CDecl, bool IncludeProtocols = true) { if (ObjCInterfaceDecl *IDecl = dyn_cast(CDecl)) { - for (auto *Prop : IDecl->properties()) + for (auto *Prop : IDecl->instance_properties()) PropMap[Prop->getIdentifier()] = Prop; // Collect the properties from visible extensions. @@ -1535,7 +1535,7 @@ static void CollectImmediateProperties(ObjCContainerDecl *CDecl, } } if (ObjCCategoryDecl *CATDecl = dyn_cast(CDecl)) { - for (auto *Prop : CATDecl->properties()) + for (auto *Prop : CATDecl->instance_properties()) PropMap[Prop->getIdentifier()] = Prop; if (IncludeProtocols) { // Scan through class's protocols. @@ -1544,7 +1544,7 @@ static void CollectImmediateProperties(ObjCContainerDecl *CDecl, } } else if (ObjCProtocolDecl *PDecl = dyn_cast(CDecl)) { - for (auto *Prop : PDecl->properties()) { + for (auto *Prop : PDecl->instance_properties()) { ObjCPropertyDecl *PropertyFromSuper = SuperPropMap[Prop->getIdentifier()]; // Exclude property for protocols which conform to class's super-class, // as super-class has to implement the property. @@ -1590,7 +1590,7 @@ Sema::IvarBacksCurrentMethodAccessor(ObjCInterfaceDecl *IFace, // look up a property declaration whose one of its accessors is implemented // by this method. - for (const auto *Property : IFace->properties()) { + for (const auto *Property : IFace->instance_properties()) { if ((Property->getGetterName() == IMD->getSelector() || Property->getSetterName() == IMD->getSelector()) && (Property->getPropertyIvarDecl() == IV)) @@ -1599,7 +1599,7 @@ Sema::IvarBacksCurrentMethodAccessor(ObjCInterfaceDecl *IFace, // Also look up property declaration in class extension whose one of its // accessors is implemented by this method. for (const auto *Ext : IFace->known_extensions()) - for (const auto *Property : Ext->properties()) + for (const auto *Property : Ext->instance_properties()) if ((Property->getGetterName() == IMD->getSelector() || Property->getSetterName() == IMD->getSelector()) && (Property->getPropertyIvarDecl() == IV)) @@ -1806,7 +1806,7 @@ void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl, } // Add the properties of 'PDecl' to the list of properties that // need to be implemented. - for (auto *PropDecl : PDecl->properties()) { + for (auto *PropDecl : PDecl->instance_properties()) { if ((*LazyMap)[PropDecl->getIdentifier()]) continue; PropMap[PropDecl->getIdentifier()] = PropDecl; @@ -1893,10 +1893,10 @@ Sema::AtomicPropertySetterGetterRules (ObjCImplDecl* IMPDecl, if (getLangOpts().getGC() != LangOptions::NonGC) return; ObjCContainerDecl::PropertyMap PM; - for (auto *Prop : IDecl->properties()) + for (auto *Prop : IDecl->instance_properties()) PM[Prop->getIdentifier()] = Prop; for (const auto *Ext : IDecl->known_extensions()) - for (auto *Prop : Ext->properties()) + for (auto *Prop : Ext->instance_properties()) PM[Prop->getIdentifier()] = Prop; for (ObjCContainerDecl::PropertyMap::iterator I = PM.begin(), E = PM.end(); diff --git a/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp b/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp index ad478cbf782..5efb9096f2f 100644 --- a/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp +++ b/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp @@ -123,7 +123,7 @@ void DirectIvarAssignment::checkASTDecl(const ObjCImplementationDecl *D, IvarToPropertyMapTy IvarToPropMap; // Find all properties for this class. - for (const auto *PD : InterD->properties()) { + for (const auto *PD : InterD->instance_properties()) { // Find the corresponding IVar. const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterD, Mgr.getASTContext()); From 681fafc445cf473176424a5dd2579426c11f8fa5 Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Tue, 26 Jan 2016 18:52:43 +0000 Subject: [PATCH 051/742] Class Property: parse property attribute (class). This is the third patch in a series of patches to support class properties in addition to instance properties in objective-c. rdar://23891898 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258834 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 337ac58fc055f4b91051cedb3a689636bcfbcffc) --- include/clang/AST/DeclObjC.h | 46 ++++++++++++++++++++++++++----- include/clang/Sema/DeclSpec.h | 5 ++-- lib/AST/ASTDumper.cpp | 2 ++ lib/AST/DeclPrinter.cpp | 5 ++++ lib/Parse/ParseObjc.cpp | 3 ++ lib/Sema/SemaObjCProperty.cpp | 5 ++++ test/Parser/objc-class-property.m | 30 ++++++++++++++++++++ 7 files changed, 87 insertions(+), 9 deletions(-) create mode 100644 test/Parser/objc-class-property.m diff --git a/include/clang/AST/DeclObjC.h b/include/clang/AST/DeclObjC.h index c5349d59c48..18f89e16acb 100644 --- a/include/clang/AST/DeclObjC.h +++ b/include/clang/AST/DeclObjC.h @@ -716,12 +716,13 @@ class ObjCPropertyDecl : public NamedDecl { /// property attribute rather than a type qualifier. OBJC_PR_nullability = 0x1000, OBJC_PR_null_resettable = 0x2000, + OBJC_PR_class = 0x4000 // Adding a property should change NumPropertyAttrsBits }; enum { /// \brief Number of bits fitting all the property attributes. - NumPropertyAttrsBits = 14 + NumPropertyAttrsBits = 15 }; enum SetterKind { Assign, Retain, Copy, Weak }; @@ -823,6 +824,9 @@ class ObjCPropertyDecl : public NamedDecl { (OBJC_PR_retain | OBJC_PR_strong | OBJC_PR_copy)); } + bool isInstanceProperty() const { return !isClassProperty(); } + bool isClassProperty() const { return PropertyAttributes & OBJC_PR_class; } + /// getSetterKind - Return the method used for doing assignment in /// the property setter. This is only valid if the property has been /// defined to have a setter. @@ -899,21 +903,49 @@ class ObjCContainerDecl : public NamedDecl, public DeclContext { SourceLocation atStartLoc) : NamedDecl(DK, DC, nameLoc, Id), DeclContext(DK), AtStart(atStartLoc) {} - // Iterator access to properties. + // Iterator access to instance/class properties. typedef specific_decl_iterator prop_iterator; typedef llvm::iterator_range> prop_range; - prop_range instance_properties() const { - return prop_range(instprop_begin(), instprop_end()); - } - prop_iterator instprop_begin() const { + prop_range properties() const { return prop_range(prop_begin(), prop_end()); } + prop_iterator prop_begin() const { return prop_iterator(decls_begin()); } - prop_iterator instprop_end() const { + prop_iterator prop_end() const { return prop_iterator(decls_end()); } + typedef filtered_decl_iterator + instprop_iterator; + typedef llvm::iterator_range instprop_range; + + instprop_range instance_properties() const { + return instprop_range(instprop_begin(), instprop_end()); + } + instprop_iterator instprop_begin() const { + return instprop_iterator(decls_begin()); + } + instprop_iterator instprop_end() const { + return instprop_iterator(decls_end()); + } + + typedef filtered_decl_iterator + classprop_iterator; + typedef llvm::iterator_range classprop_range; + + classprop_range class_properties() const { + return classprop_range(classprop_begin(), classprop_end()); + } + classprop_iterator classprop_begin() const { + return classprop_iterator(decls_begin()); + } + classprop_iterator classprop_end() const { + return classprop_iterator(decls_end()); + } + // Iterator access to instance/class methods. typedef specific_decl_iterator method_iterator; typedef llvm::iterator_range> diff --git a/include/clang/Sema/DeclSpec.h b/include/clang/Sema/DeclSpec.h index 064d37b2a02..fd50f51811e 100644 --- a/include/clang/Sema/DeclSpec.h +++ b/include/clang/Sema/DeclSpec.h @@ -800,7 +800,8 @@ class ObjCDeclSpec { DQ_PR_strong = 0x400, DQ_PR_unsafe_unretained = 0x800, DQ_PR_nullability = 0x1000, - DQ_PR_null_resettable = 0x2000 + DQ_PR_null_resettable = 0x2000, + DQ_PR_class = 0x4000 }; ObjCDeclSpec() @@ -860,7 +861,7 @@ class ObjCDeclSpec { ObjCDeclQualifier objcDeclQualifier : 7; // NOTE: VC++ treats enums as signed, avoid using ObjCPropertyAttributeKind - unsigned PropertyAttributes : 14; + unsigned PropertyAttributes : 15; unsigned Nullability : 2; diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 4622a75ac2c..9b7944271af 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -1597,6 +1597,8 @@ void ASTDumper::VisitObjCPropertyDecl(const ObjCPropertyDecl *D) { OS << " strong"; if (Attrs & ObjCPropertyDecl::OBJC_PR_unsafe_unretained) OS << " unsafe_unretained"; + if (Attrs & ObjCPropertyDecl::OBJC_PR_class) + OS << " class"; if (Attrs & ObjCPropertyDecl::OBJC_PR_getter) dumpDeclRef(D->getGetterMethodDecl(), "getter"); if (Attrs & ObjCPropertyDecl::OBJC_PR_setter) diff --git a/lib/AST/DeclPrinter.cpp b/lib/AST/DeclPrinter.cpp index 5c6002d55c0..e20c7d8c59a 100644 --- a/lib/AST/DeclPrinter.cpp +++ b/lib/AST/DeclPrinter.cpp @@ -1298,6 +1298,11 @@ void DeclPrinter::VisitObjCPropertyDecl(ObjCPropertyDecl *PDecl) { } } + if (PDecl->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_class) { + Out << (first ? ' ' : ',') << "class"; + first = false; + } + (void) first; // Silence dead store warning due to idiomatic code. Out << " )"; } diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index 249dffc56b2..33c422b6d6f 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/lib/Parse/ParseObjc.cpp @@ -849,6 +849,7 @@ static void diagnoseRedundantPropertyNullability(Parser &P, /// nullable /// null_unspecified /// null_resettable +/// class /// void Parser::ParseObjCPropertyAttribute(ObjCDeclSpec &DS) { assert(Tok.getKind() == tok::l_paren); @@ -964,6 +965,8 @@ void Parser::ParseObjCPropertyAttribute(ObjCDeclSpec &DS) { // Also set the null_resettable bit. DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_null_resettable); + } else if (II->isStr("class")) { + DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_class); } else { Diag(AttrName, diag::err_objc_expected_property_attr) << II; SkipUntil(tok::r_paren, StopAtSemi); diff --git a/lib/Sema/SemaObjCProperty.cpp b/lib/Sema/SemaObjCProperty.cpp index db37a754791..9a976e6e590 100644 --- a/lib/Sema/SemaObjCProperty.cpp +++ b/lib/Sema/SemaObjCProperty.cpp @@ -303,6 +303,8 @@ makePropertyAttributesAsWritten(unsigned Attributes) { attributesAsWritten |= ObjCPropertyDecl::OBJC_PR_nonatomic; if (Attributes & ObjCDeclSpec::DQ_PR_atomic) attributesAsWritten |= ObjCPropertyDecl::OBJC_PR_atomic; + if (Attributes & ObjCDeclSpec::DQ_PR_class) + attributesAsWritten |= ObjCPropertyDecl::OBJC_PR_class; return (ObjCPropertyDecl::PropertyAttributeKind)attributesAsWritten; } @@ -691,6 +693,9 @@ ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S, if (Attributes & ObjCDeclSpec::DQ_PR_null_resettable) PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_null_resettable); + if (Attributes & ObjCDeclSpec::DQ_PR_class) + PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_class); + return PDecl; } diff --git a/test/Parser/objc-class-property.m b/test/Parser/objc-class-property.m new file mode 100644 index 00000000000..202352c33b7 --- /dev/null +++ b/test/Parser/objc-class-property.m @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +// expected-no-diagnostics + +@interface Root +-(id) alloc; +-(id) init; +@end + +@interface A : Root { + int x; + int z; +} +@property int x; +@property int y; +@property int z; +@property(readonly) int ro, ro2; +@property (class) int c; +@property (class) int c2; +@end + +@implementation A +@dynamic x; +@synthesize z; +@dynamic c; +@end + +int test() { + A *a = [[A alloc] init]; + return a.x; +} From 28759ef772ab643948403e161cafd3cecf168263 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Tue, 26 Jan 2016 23:58:48 +0000 Subject: [PATCH 052/742] [analyzer] Body farm: Look for property ivar in shadowing readwrite property. After r251874, readonly properties that are shadowed by a readwrite property in a class extension no longer have an instance variable, which caused the body farm to not synthesize getters. Now, if a readonly property does not have an instance variable look for a shadowing property and try to get the instance variable from there. rdar://problem/24060091 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258886 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 609444fdf26450fc530f2fcc72b2c1a743e03bfd) --- lib/Analysis/BodyFarm.cpp | 41 +++++++++++++++++++++++++++++++++++++- test/Analysis/properties.m | 27 +++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/lib/Analysis/BodyFarm.cpp b/lib/Analysis/BodyFarm.cpp index 09904369ba9..325c3261374 100644 --- a/lib/Analysis/BodyFarm.cpp +++ b/lib/Analysis/BodyFarm.cpp @@ -383,10 +383,49 @@ Stmt *BodyFarm::getBody(const FunctionDecl *D) { return Val.getValue(); } +static const ObjCIvarDecl *findBackingIvar(const ObjCPropertyDecl *Prop) { + const ObjCIvarDecl *IVar = Prop->getPropertyIvarDecl(); + + if (IVar) + return IVar; + + // When a readonly property is shadowed in a class extensions with a + // a readwrite property, the instance variable belongs to the shadowing + // property rather than the shadowed property. If there is no instance + // variable on a readonly property, check to see whether the property is + // shadowed and if so try to get the instance variable from shadowing + // property. + if (!Prop->isReadOnly()) + return nullptr; + + auto *Container = cast(Prop->getDeclContext()); + const ObjCInterfaceDecl *PrimaryInterface = nullptr; + if (auto *InterfaceDecl = dyn_cast(Container)) { + PrimaryInterface = InterfaceDecl; + } else if (auto *CategoryDecl = dyn_cast(Container)) { + PrimaryInterface = CategoryDecl->getClassInterface(); + } else if (auto *ImplDecl = dyn_cast(Container)) { + PrimaryInterface = ImplDecl->getClassInterface(); + } else { + return nullptr; + } + + // FindPropertyVisibleInPrimaryClass() looks first in class extensions, so it + // is guaranteed to find the shadowing property, if it exists, rather than + // the shadowed property. + auto *ShadowingProp = PrimaryInterface->FindPropertyVisibleInPrimaryClass( + Prop->getIdentifier()); + if (ShadowingProp && ShadowingProp != Prop) { + IVar = ShadowingProp->getPropertyIvarDecl(); + } + + return IVar; +} + static Stmt *createObjCPropertyGetter(ASTContext &Ctx, const ObjCPropertyDecl *Prop) { // First, find the backing ivar. - const ObjCIvarDecl *IVar = Prop->getPropertyIvarDecl(); + const ObjCIvarDecl *IVar = findBackingIvar(Prop); if (!IVar) return nullptr; diff --git a/test/Analysis/properties.m b/test/Analysis/properties.m index bf9424c8c20..4fdbb69d87a 100644 --- a/test/Analysis/properties.m +++ b/test/Analysis/properties.m @@ -211,6 +211,33 @@ void testConsistencyAssign(Person *p) { clang_analyzer_eval(p.friend == origFriend); // expected-warning{{UNKNOWN}} } +@interface ClassWithShadowedReadWriteProperty { + int _f; +} +@property (readonly) int someProp; +@end + +@interface ClassWithShadowedReadWriteProperty () +@property (readwrite) int someProp; +@end + +@implementation ClassWithShadowedReadWriteProperty +- (void)testSynthesisForShadowedReadWriteProperties; { + clang_analyzer_eval(self.someProp == self.someProp); // expected-warning{{TRUE}} + + _f = 1; + + // Read of shadowed property should not invalidate receiver. + (void)self.someProp; + clang_analyzer_eval(_f == 1); // expected-warning{{TRUE}} + + _f = 2; + // Call to getter of shadowed property should not invalidate receiver. + (void)[self someProp]; + clang_analyzer_eval(_f == 2); // expected-warning{{TRUE}} +} +@end + #if !__has_feature(objc_arc) void testOverrelease(Person *p, int coin) { switch (coin) { From fb1b135a3727525fe75c0ccefe6a6a6ab06b6b8a Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Wed, 27 Jan 2016 01:41:58 +0000 Subject: [PATCH 053/742] [analyzer] ObjCDeallocChecker: Only operate on classes with retained properties. Previously the ObjC Dealloc Checker only checked classes with ivars, not retained properties, which caused three bugs: - False positive warnings about a missing -dealloc method in classes with only ivars. - Missing warnings about a missing -dealloc method on classes with only properties. - Missing warnings about an over-released or under-released ivar associated with a retained property in classes with only properties. The fix is to check only classes with at least one retained synthesized property. This also exposed a bug when reporting an over-released or under-released property that did not contain a synthesize statement. The checker tried to associate the warning with an @synthesize statement that did not exist, which caused an assertion failure in debug builds. The fix is to fall back to the @property statement in this case. A patch by David Kilzer! Part of rdar://problem/6927496 Differential Revision: http://reviews.llvm.org/D5023 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258896 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit e8acae8d65b63937e237511d72f2243d930afb44) --- .../Checkers/CheckObjCDealloc.cpp | 114 ++++++----- test/Analysis/DeallocMissingRelease.m | 192 ++++++++++++++++++ test/Analysis/MissingDealloc.m | 98 ++++++--- test/Analysis/PR2978.m | 11 +- 4 files changed, 328 insertions(+), 87 deletions(-) create mode 100644 test/Analysis/DeallocMissingRelease.m diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index 25caa000259..d17a8b53978 100644 --- a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -28,7 +28,7 @@ using namespace clang; using namespace ento; -static bool scan_ivar_release(Stmt *S, ObjCIvarDecl *ID, +static bool scan_ivar_release(Stmt *S, const ObjCIvarDecl *ID, const ObjCPropertyDecl *PD, Selector Release, IdentifierInfo* SelfII, @@ -76,42 +76,67 @@ static bool scan_ivar_release(Stmt *S, ObjCIvarDecl *ID, return false; } +static bool isSynthesizedRetainableProperty(const ObjCPropertyImplDecl *I, + const ObjCIvarDecl **ID, + const ObjCPropertyDecl **PD) { + + if (I->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) + return false; + + (*ID) = I->getPropertyIvarDecl(); + if (!(*ID)) + return false; + + QualType T = (*ID)->getType(); + if (!T->isObjCRetainableType()) + return false; + + (*PD) = I->getPropertyDecl(); + // Shouldn't be able to synthesize a property that doesn't exist. + assert(*PD); + + return true; +} + +static bool synthesizedPropertyRequiresRelease(const ObjCPropertyDecl *PD) { + // A synthesized property must be released if and only if the kind of setter + // was neither 'assign' or 'weak'. + ObjCPropertyDecl::SetterKind SK = PD->getSetterKind(); + return (SK != ObjCPropertyDecl::Assign && SK != ObjCPropertyDecl::Weak); +} + static void checkObjCDealloc(const CheckerBase *Checker, const ObjCImplementationDecl *D, const LangOptions &LOpts, BugReporter &BR) { - assert (LOpts.getGC() != LangOptions::GCOnly); + assert(LOpts.getGC() != LangOptions::GCOnly); + assert(!LOpts.ObjCAutoRefCount); ASTContext &Ctx = BR.getContext(); const ObjCInterfaceDecl *ID = D->getClassInterface(); - // Does the class contain any ivars that are pointers (or id<...>)? + // Does the class contain any synthesized properties that are retainable? // If not, skip the check entirely. - // NOTE: This is motivated by PR 2517: - // http://llvm.org/bugs/show_bug.cgi?id=2517 - - bool containsPointerIvar = false; - - for (const auto *Ivar : ID->ivars()) { - QualType T = Ivar->getType(); - - if (!T->isObjCObjectPointerType() || - Ivar->hasAttr() || // Skip IBOutlets. - Ivar->hasAttr()) // Skip IBOutletCollections. + bool containsRetainedSynthesizedProperty = false; + for (const auto *I : D->property_impls()) { + const ObjCIvarDecl *ID = nullptr; + const ObjCPropertyDecl *PD = nullptr; + if (!isSynthesizedRetainableProperty(I, &ID, &PD)) continue; - containsPointerIvar = true; - break; + if (synthesizedPropertyRequiresRelease(PD)) { + containsRetainedSynthesizedProperty = true; + break; + } } - if (!containsPointerIvar) + if (!containsRetainedSynthesizedProperty) return; // Determine if the class subclasses NSObject. IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject"); IdentifierInfo* SenTestCaseII = &Ctx.Idents.get("SenTestCase"); - for ( ; ID ; ID = ID->getSuperClass()) { IdentifierInfo *II = ID->getIdentifier(); @@ -142,9 +167,6 @@ static void checkObjCDealloc(const CheckerBase *Checker, } } - PathDiagnosticLocation DLoc = - PathDiagnosticLocation::createBegin(D, BR.getSourceManager()); - if (!MD) { // No dealloc found. const char* name = LOpts.getGC() == LangOptions::NonGC @@ -155,6 +177,9 @@ static void checkObjCDealloc(const CheckerBase *Checker, llvm::raw_string_ostream os(buf); os << "Objective-C class '" << *D << "' lacks a 'dealloc' instance method"; + PathDiagnosticLocation DLoc = + PathDiagnosticLocation::createBegin(D, BR.getSourceManager()); + BR.EmitBasicReport(D, Checker, name, categories::CoreFoundationObjectiveC, os.str(), DLoc); return; @@ -170,28 +195,12 @@ static void checkObjCDealloc(const CheckerBase *Checker, // Scan for missing and extra releases of ivars used by implementations // of synthesized properties for (const auto *I : D->property_impls()) { - // We can only check the synthesized properties - if (I->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) - continue; - - ObjCIvarDecl *ID = I->getPropertyIvarDecl(); - if (!ID) - continue; - - QualType T = ID->getType(); - if (!T->isObjCObjectPointerType()) // Skip non-pointer ivars - continue; - - const ObjCPropertyDecl *PD = I->getPropertyDecl(); - if (!PD) - continue; - - // ivars cannot be set via read-only properties, so we'll skip them - if (PD->isReadOnly()) + const ObjCIvarDecl *ID = nullptr; + const ObjCPropertyDecl *PD = nullptr; + if (!isSynthesizedRetainableProperty(I, &ID, &PD)) continue; - // ivar must be released if and only if the kind of setter was not 'assign' - bool requiresRelease = PD->getSetterKind() != ObjCPropertyDecl::Assign; + bool requiresRelease = synthesizedPropertyRequiresRelease(PD); if (scan_ivar_release(MD->getBody(), ID, PD, RS, SelfII, Ctx) != requiresRelease) { const char *name = nullptr; @@ -203,24 +212,28 @@ static void checkObjCDealloc(const CheckerBase *Checker, ? "missing ivar release (leak)" : "missing ivar release (Hybrid MM, non-GC)"; - os << "The '" << *ID - << "' instance variable was retained by a synthesized property but " - "wasn't released in 'dealloc'"; + os << "The '" << *ID << "' instance variable in '" << *D + << "' was retained by a synthesized property " + "but was not released in 'dealloc'"; } else { name = LOpts.getGC() == LangOptions::NonGC ? "extra ivar release (use-after-release)" : "extra ivar release (Hybrid MM, non-GC)"; - os << "The '" << *ID - << "' instance variable was not retained by a synthesized property " + os << "The '" << *ID << "' instance variable in '" << *D + << "' was not retained by a synthesized property " "but was released in 'dealloc'"; } - PathDiagnosticLocation SDLoc = - PathDiagnosticLocation::createBegin(I, BR.getSourceManager()); + // If @synthesize statement is missing, fall back to @property statement. + const Decl *SPDecl = I->getLocation().isValid() + ? static_cast(I) + : static_cast(PD); + PathDiagnosticLocation SPLoc = + PathDiagnosticLocation::createBegin(SPDecl, BR.getSourceManager()); BR.EmitBasicReport(MD, Checker, name, - categories::CoreFoundationObjectiveC, os.str(), SDLoc); + categories::CoreFoundationObjectiveC, os.str(), SPLoc); } } } @@ -235,7 +248,8 @@ class ObjCDeallocChecker : public Checker< public: void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr, BugReporter &BR) const { - if (mgr.getLangOpts().getGC() == LangOptions::GCOnly) + if (mgr.getLangOpts().getGC() == LangOptions::GCOnly || + mgr.getLangOpts().ObjCAutoRefCount) return; checkObjCDealloc(this, cast(D), mgr.getLangOpts(), BR); diff --git a/test/Analysis/DeallocMissingRelease.m b/test/Analysis/DeallocMissingRelease.m new file mode 100644 index 00000000000..3a2b556c11d --- /dev/null +++ b/test/Analysis/DeallocMissingRelease.m @@ -0,0 +1,192 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.osx.cocoa.Dealloc -fblocks %s 2>&1 | FileCheck -check-prefix=CHECK %s +// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.osx.cocoa.Dealloc -fblocks -triple x86_64-apple-darwin10 -fobjc-arc -fobjc-runtime-has-weak %s 2>&1 | FileCheck -check-prefix=CHECK-ARC -allow-empty '--implicit-check-not=error:' '--implicit-check-not=warning:' %s + +#define nil ((id)0) + +#define NON_ARC !__has_feature(objc_arc) + +#if NON_ARC +#define WEAK_ON_ARC +#else +#define WEAK_ON_ARC __weak +#endif + +typedef signed char BOOL; +@protocol NSObject +- (BOOL)isEqual:(id)object; +- (Class)class; +@end + +@interface NSObject {} +- (void)dealloc; +- (id)init; +- (id)retain; +- (oneway void)release; +@end + +typedef struct objc_selector *SEL; + +//===------------------------------------------------------------------------=== +// Do not warn about missing release in -dealloc for ivars. + +@interface MyIvarClass1 : NSObject { + NSObject *_ivar; +} +@end + +@implementation MyIvarClass1 +- (instancetype)initWithIvar:(NSObject *)ivar +{ + self = [super init]; + if (!self) + return nil; +#if NON_ARC + _ivar = [ivar retain]; +#endif + return self; +} +- (void)dealloc +{ +#if NON_ARC + [super dealloc]; +#endif +} +@end + +@interface MyIvarClass2 : NSObject { + NSObject *_ivar; +} +- (NSObject *)ivar; +- (void)setIvar:(NSObject *)ivar; +@end + +@implementation MyIvarClass2 +- (instancetype)init +{ + self = [super init]; + return self; +} +- (void)dealloc +{ +#if NON_ARC + [super dealloc]; +#endif +} +- (NSObject *)ivar +{ + return _ivar; +} +- (void)setIvar:(NSObject *)ivar +{ +#if NON_ARC + [_ivar release]; + _ivar = [ivar retain]; +#else + _ivar = ivar; +#endif +} +@end + +//===------------------------------------------------------------------------=== +// Warn about missing release in -dealloc for properties. + +@interface MyPropertyClass1 : NSObject +// CHECK: DeallocMissingRelease.m:[[@LINE+1]]:1: warning: The '_ivar' instance variable in 'MyPropertyClass1' was retained by a synthesized property but was not released in 'dealloc' +@property (copy) NSObject *ivar; +@end + +@implementation MyPropertyClass1 +- (void)dealloc +{ +#if NON_ARC + [super dealloc]; +#endif +} +@end + +@interface MyPropertyClass2 : NSObject +// CHECK: DeallocMissingRelease.m:[[@LINE+1]]:1: warning: The '_ivar' instance variable in 'MyPropertyClass2' was retained by a synthesized property but was not released in 'dealloc' +@property (retain) NSObject *ivar; +@end + +@implementation MyPropertyClass2 +- (void)dealloc +{ +#if NON_ARC + [super dealloc]; +#endif +} +@end + +@interface MyPropertyClass3 : NSObject { + NSObject *_ivar; +} +@property (retain) NSObject *ivar; +@end + +@implementation MyPropertyClass3 +// CHECK: DeallocMissingRelease.m:[[@LINE+1]]:1: warning: The '_ivar' instance variable in 'MyPropertyClass3' was retained by a synthesized property but was not released in 'dealloc' +@synthesize ivar = _ivar; +- (void)dealloc +{ +#if NON_ARC + [super dealloc]; +#endif +} +@end + +@interface MyPropertyClass4 : NSObject { + void (^_blockPropertyIvar)(void); +} +@property (copy) void (^blockProperty)(void); +@end + +@implementation MyPropertyClass4 +// CHECK: DeallocMissingRelease.m:[[@LINE+1]]:1: warning: The '_blockPropertyIvar' instance variable in 'MyPropertyClass4' was retained by a synthesized property but was not released in 'dealloc' +@synthesize blockProperty = _blockPropertyIvar; +- (void)dealloc +{ +#if NON_ARC + [super dealloc]; +#endif +} +@end + +@interface MyPropertyClass5 : NSObject { + WEAK_ON_ARC NSObject *_ivar; +} +@property (weak) NSObject *ivar; +@end + +@implementation MyPropertyClass5 +@synthesize ivar = _ivar; // no-warning +- (void)dealloc +{ +#if NON_ARC + [super dealloc]; +#endif +} +@end + +//===------------------------------------------------------------------------=== +// : 'myproperty' has kind 'assign' and thus the +// assignment through the setter does not perform a release. + +@interface MyObject : NSObject { + id __unsafe_unretained _myproperty; +} +@property(assign) id myproperty; +@end + +@implementation MyObject +@synthesize myproperty=_myproperty; // no-warning +- (void)dealloc { + // Don't claim that myproperty is released since it the property + // has the 'assign' attribute. + self.myproperty = 0; // no-warning +#if NON_ARC + [super dealloc]; +#endif +} +@end +// CHECK: 4 warnings generated. diff --git a/test/Analysis/MissingDealloc.m b/test/Analysis/MissingDealloc.m index b465959791b..d6af44b895b 100644 --- a/test/Analysis/MissingDealloc.m +++ b/test/Analysis/MissingDealloc.m @@ -1,5 +1,6 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.osx.cocoa.Dealloc %s -verify -// expected-no-diagnostics +// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.osx.cocoa.Dealloc -fblocks %s 2>&1 | FileCheck -check-prefix=CHECK %s +// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.osx.cocoa.Dealloc -fblocks -triple x86_64-apple-darwin10 -fobjc-arc %s 2>&1 | FileCheck -check-prefix=CHECK-ARC -allow-empty '--implicit-check-not=error:' '--implicit-check-not=warning:' %s + typedef signed char BOOL; @protocol NSObject - (BOOL)isEqual:(id)object; @@ -13,23 +14,74 @@ - (id)init; typedef struct objc_selector *SEL; -// : 'myproperty' has kind 'assign' and thus the -// assignment through the setter does not perform a release. +//===------------------------------------------------------------------------=== +// Do not warn about missing -dealloc method. Not enough context to know +// whether the ivar is retained or not. -@interface MyObject : NSObject { - id _myproperty; +@interface MissingDeallocWithIvar : NSObject { + NSObject *_ivar; } -@property(assign) id myproperty; @end -@implementation MyObject -@synthesize myproperty=_myproperty; // no-warning -- (void)dealloc { - self.myproperty = 0; - [super dealloc]; +@implementation MissingDeallocWithIvar +@end + +//===------------------------------------------------------------------------=== +// Do not warn about missing -dealloc method. These properties are not +// retained or synthesized. + +@interface MissingDeallocWithIntProperty : NSObject +@property (assign) int ivar; +@end + +@implementation MissingDeallocWithIntProperty +@end + +@interface MissingDeallocWithSELProperty : NSObject +@property (assign) SEL ivar; +@end + +@implementation MissingDeallocWithSELProperty +@end + +//===------------------------------------------------------------------------=== +// Warn about missing -dealloc method. + +@interface MissingDeallocWithCopyProperty : NSObject +@property (copy) NSObject *ivar; +@end + +// CHECK: MissingDealloc.m:[[@LINE+1]]:1: warning: Objective-C class 'MissingDeallocWithCopyProperty' lacks a 'dealloc' instance method +@implementation MissingDeallocWithCopyProperty +@end + +@interface MissingDeallocWithRetainProperty : NSObject +@property (retain) NSObject *ivar; +@end + +// CHECK: MissingDealloc.m:[[@LINE+1]]:1: warning: Objective-C class 'MissingDeallocWithRetainProperty' lacks a 'dealloc' instance method +@implementation MissingDeallocWithRetainProperty +@end + +@interface MissingDeallocWithIVarAndRetainProperty : NSObject { + NSObject *_ivar2; } +@property (retain) NSObject *ivar1; +@end + +// CHECK: MissingDealloc.m:[[@LINE+1]]:1: warning: Objective-C class 'MissingDeallocWithIVarAndRetainProperty' lacks a 'dealloc' instance method +@implementation MissingDeallocWithIVarAndRetainProperty +@end + +@interface MissingDeallocWithReadOnlyRetainedProperty : NSObject +@property (readonly,retain) NSObject *ivar; +@end + +// CHECK: MissingDealloc.m:[[@LINE+1]]:1: warning: Objective-C class 'MissingDeallocWithReadOnlyRetainedProperty' lacks a 'dealloc' instance method +@implementation MissingDeallocWithReadOnlyRetainedProperty @end + //===------------------------------------------------------------------------=== // Don't warn about iVars that are selectors. @@ -64,27 +116,6 @@ @interface HasOutlet : NSObject { @implementation HasOutlet // no-warning @end -//===------------------------------------------------------------------------=== -// -// Was bogus warning: "The '_myproperty' instance variable was not retained by a -// synthesized property but was released in 'dealloc'" - -@interface MyObject_rdar6380411 : NSObject { - id _myproperty; -} -@property(assign) id myproperty; -@end - -@implementation MyObject_rdar6380411 -@synthesize myproperty=_myproperty; -- (void)dealloc { - // Don't claim that myproperty is released since it the property - // has the 'assign' attribute. - self.myproperty = 0; // no-warning - [super dealloc]; -} -@end - //===------------------------------------------------------------------------=== // PR 3187: http://llvm.org/bugs/show_bug.cgi?id=3187 // - Disable the missing -dealloc check for classes that subclass SenTestCase @@ -112,3 +143,4 @@ - (void)testXXX { // do something which uses resourcepath } @end +// CHECK: 4 warnings generated. diff --git a/test/Analysis/PR2978.m b/test/Analysis/PR2978.m index 8f76120ccbc..6d5a6dfefc2 100644 --- a/test/Analysis/PR2978.m +++ b/test/Analysis/PR2978.m @@ -14,6 +14,7 @@ @interface MyClass : NSObject { id _Y; id _Z; id _K; + id _L; id _N; id _M; id _V; @@ -23,6 +24,7 @@ @interface MyClass : NSObject { @property(retain) id Y; @property(assign) id Z; @property(assign) id K; +@property(weak) id L; @property(readonly) id N; @property(retain) id M; @property(retain) id V; @@ -33,13 +35,14 @@ -(void) setO: (id) arg; @implementation MyClass @synthesize X = _X; -@synthesize Y = _Y; // expected-warning{{The '_Y' instance variable was retained by a synthesized property but wasn't released in 'dealloc'}} -@synthesize Z = _Z; // expected-warning{{The '_Z' instance variable was not retained by a synthesized property but was released in 'dealloc'}} +@synthesize Y = _Y; // expected-warning{{The '_Y' instance variable in 'MyClass' was retained by a synthesized property but was not released in 'dealloc'}} +@synthesize Z = _Z; // expected-warning{{The '_Z' instance variable in 'MyClass' was not retained by a synthesized property but was released in 'dealloc'}} @synthesize K = _K; -@synthesize N = _N; +@synthesize L = _L; // no-warning +@synthesize N = _N; // expected-warning{{The '_N' instance variable in 'MyClass' was not retained by a synthesized property but was released in 'dealloc'}} @synthesize M = _M; @synthesize V = _V; -@synthesize W = _W; // expected-warning{{The '_W' instance variable was retained by a synthesized property but wasn't released in 'dealloc'}} +@synthesize W = _W; // expected-warning{{The '_W' instance variable in 'MyClass' was retained by a synthesized property but was not released in 'dealloc'}} -(id) O{ return 0; } -(void) setO:(id)arg { } From 68c3b868051b6a85fd6279756d3195799e597068 Mon Sep 17 00:00:00 2001 From: Benjamin Kramer Date: Wed, 27 Jan 2016 10:01:30 +0000 Subject: [PATCH 054/742] Update for LLVM change. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258918 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit c723283b0b4484558077dc4e8c66dab74616b5f6) --- lib/Parse/ParseStmtAsm.cpp | 2 +- tools/driver/cc1as_main.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Parse/ParseStmtAsm.cpp b/lib/Parse/ParseStmtAsm.cpp index 4647df9a943..11b0c0a86c9 100644 --- a/lib/Parse/ParseStmtAsm.cpp +++ b/lib/Parse/ParseStmtAsm.cpp @@ -23,10 +23,10 @@ #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCObjectFileInfo.h" #include "llvm/MC/MCParser/MCAsmParser.h" +#include "llvm/MC/MCParser/MCTargetAsmParser.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSubtargetInfo.h" -#include "llvm/MC/MCTargetAsmParser.h" #include "llvm/MC/MCTargetOptions.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/TargetRegistry.h" diff --git a/tools/driver/cc1as_main.cpp b/tools/driver/cc1as_main.cpp index 59b7af52174..388a7693584 100644 --- a/tools/driver/cc1as_main.cpp +++ b/tools/driver/cc1as_main.cpp @@ -30,10 +30,10 @@ #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCObjectFileInfo.h" #include "llvm/MC/MCParser/MCAsmParser.h" +#include "llvm/MC/MCParser/MCTargetAsmParser.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSubtargetInfo.h" -#include "llvm/MC/MCTargetAsmParser.h" #include "llvm/MC/MCTargetOptions.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" From 5f34c2fd8c6ae6d2fb1f9575c730e54f795a1176 Mon Sep 17 00:00:00 2001 From: Vedant Kumar Date: Thu, 28 Jan 2016 17:52:18 +0000 Subject: [PATCH 055/742] [Coverage] Use a set to track visited FileIDs (NFC) git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259061 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit f2b57cf149a82110280d12f154b0581d36d2e983) --- lib/CodeGen/CoverageMappingGen.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/CodeGen/CoverageMappingGen.cpp b/lib/CodeGen/CoverageMappingGen.cpp index 9432a7ba78d..7edd7ce86ad 100644 --- a/lib/CodeGen/CoverageMappingGen.cpp +++ b/lib/CodeGen/CoverageMappingGen.cpp @@ -15,6 +15,7 @@ #include "CodeGenFunction.h" #include "clang/AST/StmtVisitor.h" #include "clang/Lex/Lexer.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Optional.h" #include "llvm/ProfileData/CoverageMapping.h" @@ -153,14 +154,13 @@ class CoverageMappingBuilder { void gatherFileIDs(SmallVectorImpl &Mapping) { FileIDMapping.clear(); - SmallVector Visited; + llvm::SmallSet Visited; SmallVector, 8> FileLocs; for (const auto &Region : SourceRegions) { SourceLocation Loc = Region.getStartLoc(); FileID File = SM.getFileID(Loc); - if (std::find(Visited.begin(), Visited.end(), File) != Visited.end()) + if (!Visited.insert(File).second) continue; - Visited.push_back(File); unsigned Depth = 0; for (SourceLocation Parent = getIncludeOrExpansionLoc(Loc); From fcccbacbad2b15bc6b967c4083ce297ee4259fdf Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes Date: Sun, 31 Jan 2016 00:47:51 +0000 Subject: [PATCH 056/742] [Parser] Update CachedTokens while parsing ObjectiveC template argument list Consider the following ObjC++ snippet: -- @protocol PA; @protocol PB; @class NSArray; typedef int some_t; id FA(NSArray> *h, some_t group); -- This would hit an assertion in the parser after generating an annotation token while trying to update the token cache: Assertion failed: (CachedTokens[CachedLexPos-1].getLastLoc() == Tok.getAnnotationEndLoc() && "The annotation should be until the most recent cached token") ... 7 clang::Preprocessor::AnnotatePreviousCachedTokens(clang::Token const&) + 494 8 clang::Parser::TryAnnotateTypeOrScopeTokenAfterScopeSpec(bool, bool, clang::CXXScopeSpec&, bool) + 1163 9 clang::Parser::TryAnnotateTypeOrScopeToken(bool, bool) + 361 10 clang::Parser::isCXXDeclarationSpecifier(clang::Parser::TPResult, bool*) + 598 ... The cached preprocessor token in this case is: greatergreater '>>' Loc= while the annotation ("NSArray>") ends at "testcase.mm:7:25", hence the assertion. Properly update the CachedTokens during template parsing to contain two greater tokens instead of a greatergreater. Differential Revision: http://reviews.llvm.org/D15173 rdar://problem/23494277 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259311 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit a0d5dbf5e8d9532d6581c5b221d798094bc97e82) --- include/clang/Lex/Preprocessor.h | 11 +++++++++ lib/Lex/PPCaching.cpp | 26 ++++++++++++++++++++ lib/Parse/ParseTemplate.cpp | 12 +++++++++ test/Parser/objcxx11-protocol-in-template.mm | 8 ++++++ 4 files changed, 57 insertions(+) diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h index f6154b6a49c..5ffa48277f3 100644 --- a/include/clang/Lex/Preprocessor.h +++ b/include/clang/Lex/Preprocessor.h @@ -1185,6 +1185,17 @@ class Preprocessor : public RefCountedBase { return CachedTokens[CachedLexPos-1].getLastLoc(); } + /// \brief Whether \p Tok is the most recent token (`CachedLexPos - 1`) in + /// CachedTokens. + bool IsPreviousCachedToken(const Token &Tok) const; + + /// \brief Replace token in `CachedLexPos - 1` in CachedTokens by the tokens + /// in \p NewToks. + /// + /// Useful when a token needs to be split in smaller ones and CachedTokens + /// most recent token must to be updated to reflect that. + void ReplacePreviousCachedToken(ArrayRef NewToks); + /// \brief Replace the last token with an annotation token. /// /// Like AnnotateCachedTokens(), this routine replaces an diff --git a/lib/Lex/PPCaching.cpp b/lib/Lex/PPCaching.cpp index bd48ae64ab4..4742aae5c12 100644 --- a/lib/Lex/PPCaching.cpp +++ b/lib/Lex/PPCaching.cpp @@ -116,3 +116,29 @@ void Preprocessor::AnnotatePreviousCachedTokens(const Token &Tok) { } } } + +bool Preprocessor::IsPreviousCachedToken(const Token &Tok) const { + // There's currently no cached token... + if (!CachedLexPos) + return false; + + const Token LastCachedTok = CachedTokens[CachedLexPos - 1]; + if (LastCachedTok.getKind() != Tok.getKind()) + return false; + + int RelOffset = 0; + if ((!getSourceManager().isInSameSLocAddrSpace( + Tok.getLocation(), getLastCachedTokenLocation(), &RelOffset)) || + RelOffset) + return false; + + return true; +} + +void Preprocessor::ReplacePreviousCachedToken(ArrayRef NewToks) { + assert(CachedLexPos != 0 && "Expected to have some cached tokens"); + CachedTokens.insert(CachedTokens.begin() + CachedLexPos - 1, NewToks.begin(), + NewToks.end()); + CachedTokens.erase(CachedTokens.begin() + CachedLexPos - 1 + NewToks.size()); + CachedLexPos += NewToks.size() - 1; +} diff --git a/lib/Parse/ParseTemplate.cpp b/lib/Parse/ParseTemplate.cpp index 098ac6836fd..4e372e864b2 100644 --- a/lib/Parse/ParseTemplate.cpp +++ b/lib/Parse/ParseTemplate.cpp @@ -827,6 +827,7 @@ bool Parser::ParseGreaterThanInTemplateList(SourceLocation &RAngleLoc, } // Strip the initial '>' from the token. + Token PrevTok = Tok; if (RemainingToken == tok::equal && Next.is(tok::equal) && areTokensAdjacent(Tok, Next)) { // Join two adjacent '=' tokens into one, for cases like: @@ -843,6 +844,17 @@ bool Parser::ParseGreaterThanInTemplateList(SourceLocation &RAngleLoc, PP.getSourceManager(), getLangOpts())); + // The advance from '>>' to '>' in a ObjectiveC template argument list needs + // to be properly reflected in the token cache to allow correct interaction + // between annotation and backtracking. + if (ObjCGenericList && PrevTok.getKind() == tok::greatergreater && + RemainingToken == tok::greater && PP.IsPreviousCachedToken(PrevTok)) { + PrevTok.setKind(RemainingToken); + PrevTok.setLength(1); + Token NewToks[] = {PrevTok, Tok}; + PP.ReplacePreviousCachedToken(NewToks); + } + if (!ConsumeLastToken) { // Since we're not supposed to consume the '>' token, we need to push // this token and revert the current token back to the '>'. diff --git a/test/Parser/objcxx11-protocol-in-template.mm b/test/Parser/objcxx11-protocol-in-template.mm index c5c3b6c75a4..5c80ae97597 100644 --- a/test/Parser/objcxx11-protocol-in-template.mm +++ b/test/Parser/objcxx11-protocol-in-template.mm @@ -8,3 +8,11 @@ @protocol P @end vector> v; vector>> v2; + +@protocol PA; +@protocol PB; + +@class NSArray; +typedef int some_t; + +id FA(NSArray> *h, some_t group); From c2bd4aa19a4261c9117852129047357c4fa0227a Mon Sep 17 00:00:00 2001 From: Tim Northover Date: Tue, 2 Feb 2016 18:02:10 +0000 Subject: [PATCH 057/742] ARM: allow both vfma and vfms intrinsics on v7. The main purpose here is that vfma/vfms should be symmetric, and they are supported on most v7 cores. The new ArchGuard is suggested by ACLE but prophylactic for us. Almost all CPUs with NEON *will* have vfma, and the few exceptions I know of (e.g. Cortex-A8) are incorrectly modelled by Clang so can't trigger a test. Fortunately, they're getting rarer. But if we ever do support them properly arm_neon.h should now do the right thing. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259537 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/arm_neon.td | 7 +++++-- test/Sema/arm_vfma.c | 12 ++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 test/Sema/arm_vfma.c diff --git a/include/clang/Basic/arm_neon.td b/include/clang/Basic/arm_neon.td index 6d95c1ec157..4863566653b 100644 --- a/include/clang/Basic/arm_neon.td +++ b/include/clang/Basic/arm_neon.td @@ -824,7 +824,10 @@ def VREINTERPRET //////////////////////////////////////////////////////////////////////////////// // Vector fused multiply-add operations -def VFMA : SInst<"vfma", "dddd", "fQf">; +let ArchGuard = "defined(__ARM_FEATURE_FMA)" in { + def VFMA : SInst<"vfma", "dddd", "fQf">; + def VFMS : SInst<"vfms", "dddd", "fQf">; +} //////////////////////////////////////////////////////////////////////////////// // fp16 vector operations @@ -908,7 +911,7 @@ def FDIV : IOpInst<"vdiv", "ddd", "fdQfQd", OP_DIV>; //////////////////////////////////////////////////////////////////////////////// // Vector fused multiply-add operations def FMLA : SInst<"vfma", "dddd", "dQd">; -def FMLS : SInst<"vfms", "dddd", "fdQfQd">; +def FMLS : SInst<"vfms", "dddd", "dQd">; //////////////////////////////////////////////////////////////////////////////// // MUL, MLA, MLS, FMA, FMS definitions with scalar argument diff --git a/test/Sema/arm_vfma.c b/test/Sema/arm_vfma.c new file mode 100644 index 00000000000..c50a4147b3f --- /dev/null +++ b/test/Sema/arm_vfma.c @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -triple thumbv7s-apple-ios7.0 -target-feature +neon -fsyntax-only -verify %s +#include + +// expected-no-diagnostics + +void func(float32x2_t v2f32, float32x4_t v4f32) { + vfma_f32(v2f32, v2f32, v2f32); + vfmaq_f32(v4f32, v4f32, v4f32); + + vfms_f32(v2f32, v2f32, v2f32); + vfmsq_f32(v4f32, v4f32, v4f32); +} From d6316dce449ff2f3309df28998025c34e8c8abd4 Mon Sep 17 00:00:00 2001 From: Anna Zaks Date: Fri, 29 Jan 2016 18:43:15 +0000 Subject: [PATCH 058/742] [analyzer] Improve Nullability checker diagnostics - Include the position of the argument on which the nullability is violated - Differentiate between a 'method' and a 'function' in the message wording - Test for the error message text in the tests - Fix a bug with setting 'IsDirectDereference' which resulted in regular dereferences assumed to have call context. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259221 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../Core/PathSensitive/CheckerContext.h | 4 + .../Checkers/DereferenceChecker.cpp | 4 +- .../Checkers/NullabilityChecker.cpp | 92 ++++++++++++------- lib/StaticAnalyzer/Core/CheckerContext.cpp | 7 ++ test/Analysis/nullability.mm | 56 +++++++---- test/Analysis/nullability_nullonly.mm | 8 +- 6 files changed, 114 insertions(+), 57 deletions(-) diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h index d4f014d2998..e380982d43e 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h @@ -263,6 +263,10 @@ class CheckerContext { Eng.getBugReporter().emitReport(std::move(R)); } + /// \brief Returns the word that should be used to refer to the declaration + /// in the report. + StringRef getDeclDescription(const Decl *D); + /// \brief Get the declaration of the called function (path-sensitive). const FunctionDecl *getCalleeDecl(const CallExpr *CE) const; diff --git a/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp b/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp index f216f696ef6..152b937bb03 100644 --- a/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp @@ -230,7 +230,7 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, // dereference. if (ExplodedNode *N = C.generateSink(nullState, C.getPredecessor())) { ImplicitNullDerefEvent event = {l, isLoad, N, &C.getBugReporter(), - /*IsDirectDereference=*/false}; + /*IsDirectDereference=*/true}; dispatchEvent(event); } } @@ -272,7 +272,7 @@ void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S, if (ExplodedNode *N = C.generateSink(StNull, C.getPredecessor())) { ImplicitNullDerefEvent event = {V, /*isLoad=*/true, N, &C.getBugReporter(), - /*IsDirectDereference=*/false}; + /*IsDirectDereference=*/true}; dispatchEvent(event); } } diff --git a/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp index 079a71e0174..019c6ca2b31 100644 --- a/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp @@ -26,13 +26,16 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" -#include "llvm/Support/Path.h" + #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Path.h" + using namespace clang; using namespace ento; @@ -89,18 +92,6 @@ enum class ErrorKind : int { NullablePassedToNonnull }; -const char *const ErrorMessages[] = { - "Null is assigned to a pointer which is expected to have non-null value", - "Null passed to a callee that requires a non-null argument", - "Null is returned from a function that is expected to return a non-null " - "value", - "Nullable pointer is assigned to a pointer which is expected to have " - "non-null value", - "Nullable pointer is returned from a function that is expected to return a " - "non-null value", - "Nullable pointer is dereferenced", - "Nullable pointer is passed to a callee that requires a non-null argument"}; - class NullabilityChecker : public Checker, check::PostCall, check::PostStmt, @@ -169,17 +160,19 @@ class NullabilityChecker /// /// When \p SuppressPath is set to true, no more bugs will be reported on this /// path by this checker. - void reportBugIfPreconditionHolds(ErrorKind Error, ExplodedNode *N, - const MemRegion *Region, CheckerContext &C, + void reportBugIfPreconditionHolds(StringRef Msg, ErrorKind Error, + ExplodedNode *N, const MemRegion *Region, + CheckerContext &C, const Stmt *ValueExpr = nullptr, bool SuppressPath = false) const; - void reportBug(ErrorKind Error, ExplodedNode *N, const MemRegion *Region, - BugReporter &BR, const Stmt *ValueExpr = nullptr) const { + void reportBug(StringRef Msg, ErrorKind Error, ExplodedNode *N, + const MemRegion *Region, BugReporter &BR, + const Stmt *ValueExpr = nullptr) const { if (!BT) BT.reset(new BugType(this, "Nullability", "Memory error")); - const char *Msg = ErrorMessages[static_cast(Error)]; - std::unique_ptr R(new BugReport(*BT, Msg, N)); + + auto R = llvm::make_unique(*BT, Msg, N); if (Region) { R->markInteresting(Region); R->addVisitor(llvm::make_unique(Region)); @@ -384,7 +377,7 @@ static bool checkPreconditionViolation(ProgramStateRef State, ExplodedNode *N, return false; } -void NullabilityChecker::reportBugIfPreconditionHolds( +void NullabilityChecker::reportBugIfPreconditionHolds(StringRef Msg, ErrorKind Error, ExplodedNode *N, const MemRegion *Region, CheckerContext &C, const Stmt *ValueExpr, bool SuppressPath) const { ProgramStateRef OriginalState = N->getState(); @@ -396,7 +389,7 @@ void NullabilityChecker::reportBugIfPreconditionHolds( N = C.addTransition(OriginalState, N); } - reportBug(Error, N, Region, C.getBugReporter(), ValueExpr); + reportBug(Msg, Error, N, Region, C.getBugReporter(), ValueExpr); } /// Cleaning up the program state. @@ -450,9 +443,13 @@ void NullabilityChecker::checkEvent(ImplicitNullDerefEvent Event) const { // Do not suppress errors on defensive code paths, because dereferencing // a nullable pointer is always an error. if (Event.IsDirectDereference) - reportBug(ErrorKind::NullableDereferenced, Event.SinkNode, Region, BR); - else - reportBug(ErrorKind::NullablePassedToNonnull, Event.SinkNode, Region, BR); + reportBug("Nullable pointer is dereferenced", + ErrorKind::NullableDereferenced, Event.SinkNode, Region, BR); + else { + reportBug("Nullable pointer is passed to a callee that requires a " + "non-null", ErrorKind::NullablePassedToNonnull, + Event.SinkNode, Region, BR); + } } } @@ -554,7 +551,14 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S, ExplodedNode *N = C.generateErrorNode(State, &Tag); if (!N) return; - reportBugIfPreconditionHolds(ErrorKind::NilReturnedToNonnull, N, nullptr, C, + + SmallString<256> SBuf; + llvm::raw_svector_ostream OS(SBuf); + OS << "Null is returned from a " << C.getDeclDescription(D) << + " that is expected to return a non-null value"; + + reportBugIfPreconditionHolds(OS.str(), + ErrorKind::NilReturnedToNonnull, N, nullptr, C, RetExpr); return; } @@ -573,7 +577,14 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S, RequiredNullability == Nullability::Nonnull) { static CheckerProgramPointTag Tag(this, "NullableReturnedFromNonnull"); ExplodedNode *N = C.addTransition(State, C.getPredecessor(), &Tag); - reportBugIfPreconditionHolds(ErrorKind::NullableReturnedToNonnull, N, + + SmallString<256> SBuf; + llvm::raw_svector_ostream OS(SBuf); + OS << "Nullable pointer is returned from a " << C.getDeclDescription(D) << + " that is expected to return a non-null value"; + + reportBugIfPreconditionHolds(OS.str(), + ErrorKind::NullableReturnedToNonnull, N, Region, C); } return; @@ -622,14 +633,21 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call, Nullability ArgExprTypeLevelNullability = getNullabilityAnnotation(ArgExpr->getType()); + unsigned ParamIdx = Param->getFunctionScopeIndex() + 1; + if (Filter.CheckNullPassedToNonnull && Nullness == NullConstraint::IsNull && ArgExprTypeLevelNullability != Nullability::Nonnull && RequiredNullability == Nullability::Nonnull) { ExplodedNode *N = C.generateErrorNode(State); if (!N) return; - reportBugIfPreconditionHolds(ErrorKind::NilPassedToNonnull, N, nullptr, C, - ArgExpr); + SmallString<256> SBuf; + llvm::raw_svector_ostream OS(SBuf); + OS << "Null passed to a callee that requires a non-null " << ParamIdx + << llvm::getOrdinalSuffix(ParamIdx) << " parameter"; + reportBugIfPreconditionHolds(OS.str(), ErrorKind::NilPassedToNonnull, N, + nullptr, C, + ArgExpr, /*SuppressPath=*/false); return; } @@ -648,14 +666,20 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call, if (Filter.CheckNullablePassedToNonnull && RequiredNullability == Nullability::Nonnull) { ExplodedNode *N = C.addTransition(State); - reportBugIfPreconditionHolds(ErrorKind::NullablePassedToNonnull, N, + SmallString<256> SBuf; + llvm::raw_svector_ostream OS(SBuf); + OS << "Nullable pointer is passed to a callee that requires a non-null " + << ParamIdx << llvm::getOrdinalSuffix(ParamIdx) << " parameter"; + reportBugIfPreconditionHolds(OS.str(), + ErrorKind::NullablePassedToNonnull, N, Region, C, ArgExpr, /*SuppressPath=*/true); return; } if (Filter.CheckNullableDereferenced && Param->getType()->isReferenceType()) { ExplodedNode *N = C.addTransition(State); - reportBugIfPreconditionHolds(ErrorKind::NullableDereferenced, N, Region, + reportBugIfPreconditionHolds("Nullable pointer is dereferenced", + ErrorKind::NullableDereferenced, N, Region, C, ArgExpr, /*SuppressPath=*/true); return; } @@ -1024,7 +1048,9 @@ void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S, if (!ValueExpr) ValueExpr = S; - reportBugIfPreconditionHolds(ErrorKind::NilAssignedToNonnull, N, nullptr, C, + reportBugIfPreconditionHolds("Null is assigned to a pointer which is " + "expected to have non-null value", + ErrorKind::NilAssignedToNonnull, N, nullptr, C, ValueExpr); return; } @@ -1046,7 +1072,9 @@ void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S, LocNullability == Nullability::Nonnull) { static CheckerProgramPointTag Tag(this, "NullablePassedToNonnull"); ExplodedNode *N = C.addTransition(State, C.getPredecessor(), &Tag); - reportBugIfPreconditionHolds(ErrorKind::NullableAssignedToNonnull, N, + reportBugIfPreconditionHolds("Nullable pointer is assigned to a pointer " + "which is expected to have non-null value", + ErrorKind::NullableAssignedToNonnull, N, ValueRegion, C); } return; diff --git a/lib/StaticAnalyzer/Core/CheckerContext.cpp b/lib/StaticAnalyzer/Core/CheckerContext.cpp index 5ec8bfa8007..548b06ef91f 100644 --- a/lib/StaticAnalyzer/Core/CheckerContext.cpp +++ b/lib/StaticAnalyzer/Core/CheckerContext.cpp @@ -35,6 +35,13 @@ StringRef CheckerContext::getCalleeName(const FunctionDecl *FunDecl) const { return funI->getName(); } +StringRef CheckerContext::getDeclDescription(const Decl *D) { + if (isa(D) || isa(D)) + return "method"; + if (isa(D)) + return "anonymous block"; + return "function"; +} bool CheckerContext::isCLibraryFunction(const FunctionDecl *FD, StringRef Name) { diff --git a/test/Analysis/nullability.mm b/test/Analysis/nullability.mm index b119e63ffe2..609890fbcd9 100644 --- a/test/Analysis/nullability.mm +++ b/test/Analysis/nullability.mm @@ -1,5 +1,4 @@ -// RUN: %clang_cc1 -fobjc-arc -analyze -analyzer-checker=core,nullability -verify %s -// RUN: %clang_cc1 -analyze -analyzer-checker=core,nullability -verify %s +// RUN: %clang_cc1 -fblocks -analyze -analyzer-checker=core,nullability -verify %s #define nil 0 #define BOOL int @@ -59,16 +58,16 @@ void testBasicRules() { // Make every dereference a different path to avoid sinks after errors. switch (getRandom()) { case 0: { - Dummy &r = *p; // expected-warning {{}} + Dummy &r = *p; // expected-warning {{Nullable pointer is dereferenced}} } break; case 1: { - int b = p->val; // expected-warning {{}} + int b = p->val; // expected-warning {{Nullable pointer is dereferenced}} } break; case 2: { - int stuff = *ptr; // expected-warning {{}} + int stuff = *ptr; // expected-warning {{Nullable pointer is dereferenced}} } break; case 3: - takesNonnull(p); // expected-warning {{}} + takesNonnull(p); // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}} break; case 4: { Dummy d; @@ -90,11 +89,11 @@ void testBasicRules() { Dummy *q = 0; if (getRandom()) { takesNullable(q); - takesNonnull(q); // expected-warning {{}} + takesNonnull(q); // expected-warning {{Null passed to a callee that requires a non-null 1st parameter}} } Dummy a; Dummy *_Nonnull nonnull = &a; - nonnull = q; // expected-warning {{}} + nonnull = q; // expected-warning {{Null is assigned to a pointer which is expected to have non-null value}} q = &a; takesNullable(q); takesNonnull(q); @@ -107,14 +106,14 @@ void testArgumentTracking(Dummy *_Nonnull nonnull, Dummy *_Nullable nullable) { Dummy *p = nullable; Dummy *q = nonnull; switch(getRandom()) { - case 1: nonnull = p; break; // expected-warning {{}} + case 1: nonnull = p; break; // expected-warning {{Nullable pointer is assigned to a pointer which is expected to have non-null value}} case 2: p = 0; break; case 3: q = p; break; case 4: testMultiParamChecking(nonnull, nullable, nonnull); break; case 5: testMultiParamChecking(nonnull, nonnull, nonnull); break; - case 6: testMultiParamChecking(nonnull, nullable, nullable); break; // expected-warning {{}} - case 7: testMultiParamChecking(nullable, nullable, nonnull); // expected-warning {{}} - case 8: testMultiParamChecking(nullable, nullable, nullable); // expected-warning {{}} + case 6: testMultiParamChecking(nonnull, nullable, nullable); break; // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 3rd parameter}} + case 7: testMultiParamChecking(nullable, nullable, nonnull); // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}} + case 8: testMultiParamChecking(nullable, nullable, nullable); // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}} case 9: testMultiParamChecking((Dummy *_Nonnull)0, nullable, nonnull); break; } } @@ -148,20 +147,20 @@ void testObjCMessageResultNullability() { break; case 3: shouldBeNullable = [eraseNullab(getNullableTestObject()) returnsNullable]; - [o takesNonnull:shouldBeNullable]; // expected-warning {{}} + [o takesNonnull:shouldBeNullable]; // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}} break; case 4: shouldBeNullable = [eraseNullab(getNonnullTestObject()) returnsNullable]; - [o takesNonnull:shouldBeNullable]; // expected-warning {{}} + [o takesNonnull:shouldBeNullable]; // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}} break; case 5: shouldBeNullable = [eraseNullab(getUnspecifiedTestObject()) returnsNullable]; - [o takesNonnull:shouldBeNullable]; // expected-warning {{}} + [o takesNonnull:shouldBeNullable]; // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}} break; case 6: shouldBeNullable = [eraseNullab(getNullableTestObject()) returnsNullable]; - [o takesNonnull:shouldBeNullable]; // expected-warning {{}} + [o takesNonnull:shouldBeNullable]; // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}} break; case 7: { int *shouldBeNonnull = [eraseNullab(getNonnullTestObject()) returnsNonnull]; @@ -190,7 +189,18 @@ void testObjCMessageResultNullability() { void testIndirectCastNilToNonnullAndPass() { Dummy *p = (Dummy * _Nonnull)0; // FIXME: Ideally the cast above would suppress this warning. - takesNonnull(p); // expected-warning {{Null passed to a callee that requires a non-null argument}} + takesNonnull(p); // expected-warning {{Null passed to a callee that requires a non-null 1st parameter}} +} + +void testIndirectNilPassToNonnull() { + Dummy *p = 0; + takesNonnull(p); // expected-warning {{Null passed to a callee that requires a non-null 1st parameter}} +} + +void testConditionalNilPassToNonnull(Dummy *p) { + if (!p) { + takesNonnull(p); // expected-warning {{Null passed to a callee that requires a non-null 1st parameter}} + } } Dummy * _Nonnull testIndirectCastNilToNonnullAndReturn() { @@ -207,7 +217,7 @@ void testInvalidPropagation() { void onlyReportFirstPreconditionViolationOnPath() { Dummy *p = returnsNullable(); - takesNonnull(p); // expected-warning {{}} + takesNonnull(p); // expected-warning {{Nullable pointer is passed to a callee that requires a non-null 1st parameter}} takesNonnull(p); // No warning. // The first warning was not a sink. The analysis expected to continue. int i = 0; @@ -256,6 +266,14 @@ void inlinedUnspecified(Dummy *p) { if (p) return; } +void testNilReturnWithBlock(Dummy *p) { + p = 0; + Dummy *_Nonnull (^myblock)(void) = ^Dummy *_Nonnull(void) { + return p; // TODO: We should warn in blocks. + }; + myblock(); +} + Dummy *_Nonnull testDefensiveInlineChecks(Dummy * p) { switch (getRandom()) { case 1: inlinedNullable(p); break; @@ -297,7 +315,7 @@ - (id)initWithSomething:(int)i { - (TestObject * _Nonnull)testReturnsNullableInNonnullIndirectly { TestObject *local = getNullableTestObject(); - return local; // expected-warning {{Nullable pointer is returned from a function that is expected to return a non-null value}} + return local; // expected-warning {{Nullable pointer is returned from a method that is expected to return a non-null value}} } - (TestObject * _Nonnull)testReturnsCastSuppressedNullableInNonnullIndirectly { diff --git a/test/Analysis/nullability_nullonly.mm b/test/Analysis/nullability_nullonly.mm index c9129a8067c..d82105cf5b5 100644 --- a/test/Analysis/nullability_nullonly.mm +++ b/test/Analysis/nullability_nullonly.mm @@ -31,18 +31,18 @@ void testBasicRules() { Dummy *q = 0; if (getRandom()) { takesNullable(q); - takesNonnull(q); // expected-warning {{}} + takesNonnull(q); // expected-warning {{Null passed to a callee that requires a non-null 1st parameter}} } } Dummy *_Nonnull testNullReturn() { Dummy *p = 0; - return p; // expected-warning {{}} + return p; // expected-warning {{Null is returned from a function that is expected to return a non-null value}} } void onlyReportFirstPreconditionViolationOnPath() { Dummy *p = 0; - takesNonnull(p); // expected-warning {{}} + takesNonnull(p); // expected-warning {{Null passed to a callee that requires a non-null 1st parameter}} takesNonnull(p); // No warning. // Passing null to nonnull is a sink. Stop the analysis. int i = 0; @@ -143,7 +143,7 @@ @interface SomeClass : NSObject @implementation SomeClass (MethodReturn) - (SomeClass * _Nonnull)testReturnsNilInNonnull { SomeClass *local = nil; - return local; // expected-warning {{Null is returned from a function that is expected to return a non-null value}} + return local; // expected-warning {{Null is returned from a method that is expected to return a non-null value}} } - (SomeClass * _Nonnull)testReturnsCastSuppressedNilInNonnull { From f45d7e6e60d010d12ed7b656a8ea4f9fcc4d1437 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Thu, 28 Jan 2016 22:23:34 +0000 Subject: [PATCH 059/742] [analyzer] Suppress nullability warnings in copy, mutableCopy, and init families. There are multiple, common idioms of defensive nil-checks in copy, mutableCopy, and init methods in ObjC. The analyzer doesn't currently have the capability to distinguish these idioms from true positives, so suppress all warnings about returns in those families. This is a pretty blunt suppression that we should improve later. rdar://problem/24395811 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259099 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../Checkers/NullabilityChecker.cpp | 21 ++++---- test/Analysis/nullability.mm | 54 ++++++++++++++++++- 2 files changed, 62 insertions(+), 13 deletions(-) diff --git a/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp index 019c6ca2b31..0741ae0dd7f 100644 --- a/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp @@ -507,23 +507,22 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S, if (!RetSVal) return; - bool IsReturnSelfInObjCInit = false; + bool InSuppressedMethodFamily = false; QualType RequiredRetType; AnalysisDeclContext *DeclCtxt = C.getLocationContext()->getAnalysisDeclContext(); const Decl *D = DeclCtxt->getDecl(); if (auto *MD = dyn_cast(D)) { + // HACK: This is a big hammer to avoid warning when there are defensive + // nil checks in -init and -copy methods. We should add more sophisticated + // logic here to suppress on common defensive idioms but still + // warn when there is a likely problem. + ObjCMethodFamily Family = MD->getMethodFamily(); + if (OMF_init == Family || OMF_copy == Family || OMF_mutableCopy == Family) + InSuppressedMethodFamily = true; + RequiredRetType = MD->getReturnType(); - // Suppress diagnostics for returns of nil that are syntactic returns of - // self in ObjC initializers. This avoids warning under the common idiom of - // a defensive check of the result of a call to super: - // if (self = [super init]) { - // ... - // } - // return self; // no-warning - IsReturnSelfInObjCInit = (MD->getMethodFamily() == OMF_init) && - isReturnSelf(S, C); } else if (auto *FD = dyn_cast(D)) { RequiredRetType = FD->getReturnType(); } else { @@ -546,7 +545,7 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S, Nullness == NullConstraint::IsNull && RetExprTypeLevelNullability != Nullability::Nonnull && RequiredNullability == Nullability::Nonnull && - !IsReturnSelfInObjCInit) { + !InSuppressedMethodFamily) { static CheckerProgramPointTag Tag(this, "NullReturnedFromNonnull"); ExplodedNode *N = C.generateErrorNode(State, &Tag); if (!N) diff --git a/test/Analysis/nullability.mm b/test/Analysis/nullability.mm index 609890fbcd9..220a38118aa 100644 --- a/test/Analysis/nullability.mm +++ b/test/Analysis/nullability.mm @@ -3,14 +3,27 @@ #define nil 0 #define BOOL int +#define NS_ASSUME_NONNULL_BEGIN _Pragma("clang assume_nonnull begin") +#define NS_ASSUME_NONNULL_END _Pragma("clang assume_nonnull end") + +typedef struct _NSZone NSZone; + @protocol NSObject + (id)alloc; - (id)init; @end +NS_ASSUME_NONNULL_BEGIN @protocol NSCopying +- (id)copyWithZone:(nullable NSZone *)zone; + @end +@protocol NSMutableCopying +- (id)mutableCopyWithZone:(nullable NSZone *)zone; +@end +NS_ASSUME_NONNULL_END + __attribute__((objc_root_class)) @interface NSObject @@ -350,8 +363,9 @@ - (instancetype _Nonnull)initWithNonnullReturnAndNilReturnViaLocal { // This leaks, but we're not checking for that here. ClassWithInitializers *other = nil; - // Still warn when when not returning via self. - return other; // expected-warning {{Null is returned from a function that is expected to return a non-null value}} + // False negative. Once we have more subtle suppression of defensive checks in + // initializers we should warn here. + return other; } @end @@ -368,4 +382,40 @@ - (id)initWithNonnullReturnAndSelfCheckingIdiom { return self; // no-warning } + +- (id _Nonnull)initWithNonnullReturnAndSelfCheckingIdiomV2; { + // Another common return-checking idiom + self = [super initWithNonnullReturnAndSelfCheckingIdiom]; + if (!self) { + return nil; // no-warning + } + + return self; +} +@end + +@interface ClassWithCopyWithZone : NSObject { + id i; +} + +@end + +@implementation ClassWithCopyWithZone +-(id)copyWithZone:(NSZone *)zone { + ClassWithCopyWithZone *newInstance = [[ClassWithCopyWithZone alloc] init]; + if (!newInstance) + return nil; + + newInstance->i = i; + return newInstance; +} + +-(id)mutableCopyWithZone:(NSZone *)zone { + ClassWithCopyWithZone *newInstance = [[ClassWithCopyWithZone alloc] init]; + if (newInstance) { + newInstance->i = i; + } + + return newInstance; +} @end From f7c9aec539acf6bfc26449e2350ca5a796ceff84 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Thu, 28 Jan 2016 23:34:13 +0000 Subject: [PATCH 060/742] [analyzer] NullabilityChecker: Remove unused isReturnSelf() function. Remove the now-unused isReturnSelf() function so we don't get a compiler warning. Apologies for not doing this in r259099. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259118 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../Checkers/NullabilityChecker.cpp | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp index 0741ae0dd7f..e096e2047f5 100644 --- a/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp @@ -467,22 +467,6 @@ static const Expr *lookThroughImplicitCasts(const Expr *E) { return E; } -/// Returns true when the return statement is a syntactic 'return self' in -/// Objective-C. -static bool isReturnSelf(const ReturnStmt *RS, CheckerContext &C) { - const ImplicitParamDecl *SelfDecl = - C.getCurrentAnalysisDeclContext()->getSelfDecl(); - if (!SelfDecl) - return false; - - const Expr *ReturnExpr = lookThroughImplicitCasts(RS->getRetValue()); - auto *RefExpr = dyn_cast(ReturnExpr); - if (!RefExpr) - return false; - - return RefExpr->getDecl() == SelfDecl; -} - /// This method check when nullable pointer or null value is returned from a /// function that has nonnull return type. /// From 890a451fad394f0f319cdf9e23a254020003337e Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Fri, 29 Jan 2016 18:47:13 +0000 Subject: [PATCH 061/742] [analyzer] Suppress null reports from defensive checks in function-like macros. We already do this for case splits introduced as a result of defensive null checks in functions and methods, so do the same for function-like macros. rdar://problem/19640441 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259222 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../Core/BugReporterVisitors.cpp | 27 +++++++- .../inlining/false-positive-suppression.c | 68 +++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index cf1e0a6a656..f1c3223fae4 100644 --- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -828,8 +828,33 @@ SuppressInlineDefensiveChecksVisitor::VisitNode(const ExplodedNode *Succ, // Check if this is inlined defensive checks. const LocationContext *CurLC =Succ->getLocationContext(); const LocationContext *ReportLC = BR.getErrorNode()->getLocationContext(); - if (CurLC != ReportLC && !CurLC->isParentOf(ReportLC)) + if (CurLC != ReportLC && !CurLC->isParentOf(ReportLC)) { BR.markInvalid("Suppress IDC", CurLC); + return nullptr; + } + + // Treat defensive checks in function-like macros as if they were an inlined + // defensive check. + auto CurPoint = Succ->getLocation().getAs(); + auto BugPoint = BR.getErrorNode()->getLocation().getAs(); + + if (!CurPoint || !BugPoint) + return nullptr; + + SourceLocation CurLoc = + CurPoint->getSrc()->getTerminator().getStmt()->getLocStart(); + SourceLocation BugLoc = BugPoint->getStmt()->getLocStart(); + + if (CurLoc.isMacroID() && !BugLoc.isMacroID()) { + const SourceManager &SMgr = BRC.getSourceManager(); + std::pair CLInfo = SMgr.getDecomposedLoc(CurLoc); + SrcMgr::SLocEntry SE = SMgr.getSLocEntry(CLInfo.first); + const SrcMgr::ExpansionInfo &EInfo = SE.getExpansion(); + if (EInfo.isFunctionMacroExpansion()) { + BR.markInvalid("Suppress Macro IDC", CurLC); + return nullptr; + } + } } return nullptr; } diff --git a/test/Analysis/inlining/false-positive-suppression.c b/test/Analysis/inlining/false-positive-suppression.c index e1c8f67614e..335940ab5fa 100644 --- a/test/Analysis/inlining/false-positive-suppression.c +++ b/test/Analysis/inlining/false-positive-suppression.c @@ -93,6 +93,74 @@ int triggerDivZero () { return 5/y; // expected-warning {{Division by zero}} } +// Treat a function-like macro similarly to an inlined function, so suppress +// warnings along paths resulting from inlined checks. +#define MACRO_WITH_CHECK(a) ( ((a) != 0) ? *a : 17) +void testInlineCheckInMacro(int *p) { + int i = MACRO_WITH_CHECK(p); + (void)i; + + *p = 1; // no-warning +} + +#define MACRO_WITH_NESTED_CHECK(a) ( { int j = MACRO_WITH_CHECK(a); j; } ) +void testInlineCheckInNestedMacro(int *p) { + int i = MACRO_WITH_NESTED_CHECK(p); + (void)i; + + *p = 1; // no-warning +} + +// If there is a check in a macro that is not function-like, don't treat +// it like a function so don't suppress. +#define NON_FUNCTION_MACRO_WITH_CHECK ( ((p) != 0) ? *p : 17) +void testNonFunctionMacro(int *p) { + int i = NON_FUNCTION_MACRO_WITH_CHECK ; + (void)i; + + *p = 1; // expected-warning {{Dereference of null pointer (loaded from variable 'p')}} +} + + +// This macro will dereference its argument if the argument is NULL. +#define MACRO_WITH_ERROR(a) ( ((a) != 0) ? 0 : *a) +void testErrorInMacro(int *p) { + int i = MACRO_WITH_ERROR(p); // expected-warning {{Dereference of null pointer (loaded from variable 'p')}} + (void)i; +} + +// Here the check (the "if") is not in a macro, so we should still warn. +#define MACRO_IN_GUARD(a) (!(a)) +void testMacroUsedAsGuard(int *p) { + if (MACRO_IN_GUARD(p)) + *p = 1; // expected-warning {{Dereference of null pointer (loaded from variable 'p')}} +} + +// When a nil case split is introduced in a macro and the macro is in a guard, +// we still shouldn't warn. +int isNull(int *p); +int isEqual(int *p, int *q); +#define ISNULL(ptr) ((ptr) == 0 || isNull(ptr)) +#define ISEQUAL(a, b) ((int *)(a) == (int *)(b) || (ISNULL(a) && ISNULL(b)) || isEqual(a,b)) +#define ISNOTEQUAL(a, b) (!ISEQUAL(a, b)) +void testNestedDisjunctiveMacro(int *p, int *q) { + if (ISNOTEQUAL(p,q)) { + (void)*p; // no-warning + (void)*q; // no-warning + } + + (void)*p; // no-warning + (void)*q; // no-warning +} + +// Here the check is entirely in non-macro code even though the code itself +// is a macro argument. +#define MACRO_DO_IT(a) (a) +void testErrorInArgument(int *p) { + int i = MACRO_DO_IT((p ? 0 : *p)); // expected-warning {{Dereference of null pointer (loaded from variable 'p')}}c + (void)i; +} + // -------------------------- // "Suppression suppression" // -------------------------- From e16ad45f8aaf7a827e72657bc65cde1a27f97128 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Sat, 30 Jan 2016 01:59:33 +0000 Subject: [PATCH 062/742] [analyzer] Make suppression of macro defensive checks work with -analyzer-eagerly-assume. This is the default for the analyzer but the flag is added by the driver so our suppression tests didn't cover this case. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259288 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../Core/BugReporterVisitors.cpp | 37 +++++++++++++++---- .../inlining/false-positive-suppression.c | 24 ++++++++---- 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index f1c3223fae4..ae5cd546f8d 100644 --- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -14,6 +14,7 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprObjC.h" +#include "clang/Analysis/CFGStmtMap.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" @@ -834,21 +835,41 @@ SuppressInlineDefensiveChecksVisitor::VisitNode(const ExplodedNode *Succ, } // Treat defensive checks in function-like macros as if they were an inlined - // defensive check. - auto CurPoint = Succ->getLocation().getAs(); + // defensive check. If the bug location is not in a macro and the + // terminator for the current location is in a macro then suppress the + // warning. auto BugPoint = BR.getErrorNode()->getLocation().getAs(); - if (!CurPoint || !BugPoint) + if (!BugPoint) return nullptr; - SourceLocation CurLoc = - CurPoint->getSrc()->getTerminator().getStmt()->getLocStart(); SourceLocation BugLoc = BugPoint->getStmt()->getLocStart(); + if (BugLoc.isMacroID()) + return nullptr; + + ProgramPoint CurPoint = Succ->getLocation(); + const Stmt *CurTerminatorStmt = nullptr; + if (auto BE = CurPoint.getAs()) { + CurTerminatorStmt = BE->getSrc()->getTerminator().getStmt(); + } else if (auto SP = CurPoint.getAs()) { + const Stmt *CurStmt = SP->getStmt(); + if (!CurStmt->getLocStart().isMacroID()) + return nullptr; + + CFGStmtMap *Map = CurLC->getAnalysisDeclContext()->getCFGStmtMap(); + CurTerminatorStmt = Map->getBlock(CurStmt)->getTerminator(); + } else { + return nullptr; + } + + if (!CurTerminatorStmt) + return nullptr; - if (CurLoc.isMacroID() && !BugLoc.isMacroID()) { + SourceLocation TerminatorLoc = CurTerminatorStmt->getLocStart(); + if (TerminatorLoc.isMacroID()) { const SourceManager &SMgr = BRC.getSourceManager(); - std::pair CLInfo = SMgr.getDecomposedLoc(CurLoc); - SrcMgr::SLocEntry SE = SMgr.getSLocEntry(CLInfo.first); + std::pair TLInfo = SMgr.getDecomposedLoc(TerminatorLoc); + SrcMgr::SLocEntry SE = SMgr.getSLocEntry(TLInfo.first); const SrcMgr::ExpansionInfo &EInfo = SE.getExpansion(); if (EInfo.isFunctionMacroExpansion()) { BR.markInvalid("Suppress Macro IDC", CurLC); diff --git a/test/Analysis/inlining/false-positive-suppression.c b/test/Analysis/inlining/false-positive-suppression.c index 335940ab5fa..a0bc3611fe9 100644 --- a/test/Analysis/inlining/false-positive-suppression.c +++ b/test/Analysis/inlining/false-positive-suppression.c @@ -1,6 +1,6 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-config suppress-null-return-paths=false -verify %s -// RUN: %clang_cc1 -analyze -analyzer-checker=core -verify -DSUPPRESSED=1 %s -// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-config avoid-suppressing-null-argument-paths=true -DSUPPRESSED=1 -DNULL_ARGS=1 -verify %s +// RUN: %clang_cc1 -analyze -analyzer-eagerly-assume -analyzer-checker=core -analyzer-config suppress-null-return-paths=false -verify %s +// RUN: %clang_cc1 -analyze -analyzer-eagerly-assume -analyzer-checker=core -verify -DSUPPRESSED=1 %s +// RUN: %clang_cc1 -analyze -analyzer-eagerly-assume -analyzer-checker=core -analyzer-config avoid-suppressing-null-argument-paths=true -DSUPPRESSED=1 -DNULL_ARGS=1 -verify %s int opaquePropertyCheck(void *object); int coin(); @@ -145,14 +145,24 @@ int isEqual(int *p, int *q); #define ISNOTEQUAL(a, b) (!ISEQUAL(a, b)) void testNestedDisjunctiveMacro(int *p, int *q) { if (ISNOTEQUAL(p,q)) { - (void)*p; // no-warning - (void)*q; // no-warning + *p = 1; // no-warning + *q = 1; // no-warning } - (void)*p; // no-warning - (void)*q; // no-warning + *p = 1; // no-warning + *q = 1; // no-warning +} + +void testNestedDisjunctiveMacro2(int *p, int *q) { + if (ISEQUAL(p,q)) { + return; + } + + *p = 1; // no-warning + *q = 1; // no-warning } + // Here the check is entirely in non-macro code even though the code itself // is a macro argument. #define MACRO_DO_IT(a) (a) From 35aeed5957506ea23ebbc276d37b7b7423cc9139 Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Fri, 29 Jan 2016 15:53:43 -0800 Subject: [PATCH 063/742] Introduce ns_error_domain attribute. ns_error_domain can be used by, e.g. NS_ERROR_ENUM, in order to identify a global declaration representing the domain constant. Introduces the attribute, Sema handling, diagnostics, and test case. --- include/clang/Basic/Attr.td | 6 ++++ include/clang/Basic/AttrDocs.td | 7 ++++ include/clang/Basic/DiagnosticSemaKinds.td | 7 ++++ lib/Sema/SemaDeclAttr.cpp | 37 +++++++++++++++++++ test/Analysis/ns_error_enum.m | 42 ++++++++++++++++++++++ 5 files changed, 99 insertions(+) create mode 100644 test/Analysis/ns_error_enum.m diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 48db0632261..dba24aaec50 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -1080,6 +1080,12 @@ def ObjCBridgeRelated : InheritableAttr { let Documentation = [Undocumented]; } +def NSErrorDomain : Attr { + let Spellings = [GNU<"ns_error_domain">]; + let Args = [IdentifierArgument<"ErrorDomain">]; + let Documentation = [NSErrorDomainDocs]; +} + def NSReturnsRetained : InheritableAttr { let Spellings = [GNU<"ns_returns_retained">]; // let Subjects = SubjectList<[ObjCMethod, ObjCProperty, Function]>; diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td index 628ea7c88bc..707a547c2ce 100644 --- a/include/clang/Basic/AttrDocs.td +++ b/include/clang/Basic/AttrDocs.td @@ -1621,6 +1621,13 @@ arguments, with arbitrary offsets. }]; } +def NSErrorDomainDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``ns_error_domain`` attribute indicates a global constant representing the error domain. + }]; +} + def SwiftDocs : DocumentationCategory<"Controlling Swift Import"> { let Content = [{ Clang supports additional attributes for controlling how APIs are imported into Swift. diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 0f94579fb2e..fa0b5fe85d7 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -7402,6 +7402,13 @@ def err_nsconsumed_attribute_mismatch : Error< def err_nsreturns_retained_attribute_mismatch : Error< "overriding method has mismatched ns_returns_%select{not_retained|retained}0" " attributes">; + +def err_nserrordomain_not_tagdecl : Error< + "ns_error_domain attribute only valid on enum/struct/union/class">; +def err_nserrordomain_invalid_decl : Error< + "domain argument %0 not valid top-level declaration">; +def err_nserrordomain_requires_identifier : Error< + "domain argument must be an identifier">; def note_getter_unavailable : Note< "or because setter is declared here, but no getter method %0 is found">; diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 3a7d303f111..886504ae005 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -4057,6 +4057,39 @@ static void handleObjCRequiresSuperAttr(Sema &S, Decl *D, attr.getAttributeSpellingListIndex())); } +static void handleNSErrorDomain(Sema &S, Decl *D, const AttributeList &Attr) { + if (!isa(D)) { + S.Diag(D->getLocStart(), diag::err_nserrordomain_not_tagdecl); + return; + } + IdentifierLoc *identLoc = + Attr.isArgIdent(0) ? Attr.getArgAsIdent(0) : nullptr; + if (!identLoc || !identLoc->Ident) { + // Try to locate the argument directly + SourceLocation loc = Attr.getLoc(); + if (Attr.isArgExpr(0) && Attr.getArgAsExpr(0)) + loc = Attr.getArgAsExpr(0)->getLocStart(); + + S.Diag(loc, diag::err_nserrordomain_requires_identifier); + return; + } + + // Verify that the identifier is a valid decl in the C decl namespace + LookupResult lookupResult(S, DeclarationName(identLoc->Ident), + SourceLocation(), + Sema::LookupNameKind::LookupOrdinaryName); + if (!S.LookupName(lookupResult, S.TUScope) || + !lookupResult.getAsSingle()) { + S.Diag(identLoc->Loc, diag::err_nserrordomain_invalid_decl) + << identLoc->Ident; + return; + } + + D->addAttr(::new (S.Context) + NSErrorDomainAttr(Attr.getRange(), S.Context, identLoc->Ident, + Attr.getAttributeSpellingListIndex())); +} + static void handleCFAuditedTransferAttr(Sema &S, Decl *D, const AttributeList &Attr) { if (checkAttrMutualExclusion(S, D, Attr)) @@ -5197,6 +5230,10 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_ObjCBoxable: handleObjCBoxable(S, D, Attr); break; + + case AttributeList::AT_NSErrorDomain: + handleNSErrorDomain(S, D, Attr); + break; case AttributeList::AT_CFAuditedTransfer: handleCFAuditedTransferAttr(S, D, Attr); diff --git a/test/Analysis/ns_error_enum.m b/test/Analysis/ns_error_enum.m new file mode 100644 index 00000000000..79e3c25cbaa --- /dev/null +++ b/test/Analysis/ns_error_enum.m @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -verify %s + +#define CF_ENUM(_type, _name) enum _name : _type _name; enum _name : _type +#define NS_ENUM(_type, _name) CF_ENUM(_type, _name) + +#define NS_ERROR_ENUM(_type, _name, _domain) \ + enum _name : _type _name; enum __attribute__((ns_error_domain(_domain))) _name : _type + +typedef NS_ENUM(unsigned, MyEnum) { + MyFirst, + MySecond, +}; + +typedef NS_ENUM(invalidType, MyInvalidEnum) { +// expected-error@-1{{unknown type name 'invalidType'}} +// expected-error@-2{{unknown type name 'invalidType'}} + MyFirstInvalid, + MySecondInvalid, +}; + +const char *MyErrorDomain; +typedef NS_ERROR_ENUM(unsigned char, MyErrorEnum, MyErrorDomain) { + MyErrFirst, + MyErrSecond, +}; +struct __attribute__((ns_error_domain(MyErrorDomain))) MyStructErrorDomain {}; + +typedef NS_ERROR_ENUM(unsigned char, MyErrorEnumInvalid, InvalidDomain) { + // expected-error@-1{{domain argument 'InvalidDomain' not valid top-level declaration}} + MyErrFirstInvalid, + MyErrSecondInvalid, +}; + +typedef NS_ERROR_ENUM(unsigned char, MyErrorEnumInvalid, "domain-string"); + // expected-error@-1{{domain argument must be an identifier}} + +int __attribute__((ns_error_domain(MyErrorDomain))) NotTagDecl; + // expected-error@-1{{ns_error_domain attribute only valid on enum/struct/union/class}} + +void foo() {} +typedef NS_ERROR_ENUM(unsigned char, MyErrorEnumInvalidFunction, foo); + // expected-error@-1{{domain argument 'foo' not valid top-level declaration}} From a5de0b0c3e643ff762c017b1074886867285f7bb Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Thu, 28 Jan 2016 23:29:02 +0000 Subject: [PATCH 064/742] Check for frontend errors after releasing the Builder. Frontend can emit errors when releaseing the Builder. If there are errors before or when releasing the Builder, we reset the module to stop here before invoking the backend. Before this commit, clang will continue to invoke the backend and backend can crash. Differential Revision: http://reviews.llvm.org/D16564 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259116 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/ModuleBuilder.cpp | 9 ++++++--- test/CodeGen/target-builtin-error-3.c | 28 +++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 test/CodeGen/target-builtin-error-3.c diff --git a/lib/CodeGen/ModuleBuilder.cpp b/lib/CodeGen/ModuleBuilder.cpp index bce19ab2fdb..82ad0dbabb9 100644 --- a/lib/CodeGen/ModuleBuilder.cpp +++ b/lib/CodeGen/ModuleBuilder.cpp @@ -199,15 +199,18 @@ namespace { } void HandleTranslationUnit(ASTContext &Ctx) override { + // Release the Builder when there is no error. + if (!Diags.hasErrorOccurred() && Builder) + Builder->Release(); + + // If there are errors before or when releasing the Builder, reset + // the module to stop here before invoking the backend. if (Diags.hasErrorOccurred()) { if (Builder) Builder->clear(); M.reset(); return; } - - if (Builder) - Builder->Release(); } void CompleteTentativeDefinition(VarDecl *D) override { diff --git a/test/CodeGen/target-builtin-error-3.c b/test/CodeGen/target-builtin-error-3.c new file mode 100644 index 00000000000..5beb474befe --- /dev/null +++ b/test/CodeGen/target-builtin-error-3.c @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 %s -triple=x86_64-apple-darwin -S -verify -o - -target-feature +avx + +// RUN: not %clang_cc1 %s -triple=x86_64-apple-darwin -emit-obj -target-feature +avx 2> %t.err +// RUN: FileCheck < %t.err %s +// CHECK: 1 error generated + +typedef unsigned short uint16_t; +typedef long long __m128i __attribute__((__vector_size__(16))); +typedef float __v8sf __attribute__ ((__vector_size__ (32))); +typedef float __m256 __attribute__ ((__vector_size__ (32))); +typedef uint16_t half; +typedef __attribute__ ((ext_vector_type( 8),__aligned__( 16))) half half8; +typedef __attribute__ ((ext_vector_type(16),__aligned__( 32))) half half16; +typedef __attribute__ ((ext_vector_type(16),__aligned__( 2))) half half16U; +typedef __attribute__ ((ext_vector_type( 8),__aligned__( 32))) float float8; +typedef __attribute__ ((ext_vector_type(16),__aligned__( 64))) float float16; +static inline half8 __attribute__((__overloadable__)) convert_half( float8 a ) { + return __extension__ ({ __m256 __a = (a); (__m128i)__builtin_ia32_vcvtps2ph256((__v8sf)__a, (0x00)); }); // expected-error {{'__builtin_ia32_vcvtps2ph256' needs target feature f16c}} +} +static inline half16 __attribute__((__overloadable__)) convert_half( float16 a ) { + half16 r; + r.lo = convert_half( a.lo); + return r; +} +void avx_test( uint16_t *destData, float16 argbF) +{ + ((half16U*)destData)[0] = convert_half(argbF); +} From 24268dea260dc7c6a5412fdef46aff662741a35c Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Tue, 2 Feb 2016 22:23:03 +0000 Subject: [PATCH 065/742] ObjCXX: fix a crash during typo correction. For ObjCXX, we can create a CastExpr with Kind being CK_UserDefinedConversion and SubExpr being BlockExpr. Specifically one can return BlockExpr from BuildCXXMemberCallExpr and the result can be used to build a CastExpr. Fix the assumption in CastExpr::getSubExprAsWritten that SubExpr can only be CXXMemberCallExpr. rdar://problem/24364077 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259591 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/Expr.cpp | 9 +++++-- .../SemaObjCXX/block-for-lambda-conversion.mm | 27 +++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 test/SemaObjCXX/block-for-lambda-conversion.mm diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 42d7ed3ac6f..62b799953ad 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -1744,8 +1744,13 @@ Expr *CastExpr::getSubExprAsWritten() { // subexpression describing the call; strip it off. if (E->getCastKind() == CK_ConstructorConversion) SubExpr = cast(SubExpr)->getArg(0); - else if (E->getCastKind() == CK_UserDefinedConversion) - SubExpr = cast(SubExpr)->getImplicitObjectArgument(); + else if (E->getCastKind() == CK_UserDefinedConversion) { + assert((isa(SubExpr) || + isa(SubExpr)) && + "Unexpected SubExpr for CK_UserDefinedConversion."); + if (isa(SubExpr)) + SubExpr = cast(SubExpr)->getImplicitObjectArgument(); + } // If the subexpression we're left with is an implicit cast, look // through that, too. diff --git a/test/SemaObjCXX/block-for-lambda-conversion.mm b/test/SemaObjCXX/block-for-lambda-conversion.mm new file mode 100644 index 00000000000..671e83dc220 --- /dev/null +++ b/test/SemaObjCXX/block-for-lambda-conversion.mm @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -fsyntax-only -fblocks -verify -std=c++11 %s + +enum NSEventType { + NSEventTypeFlagsChanged = 12 +}; + +enum NSEventMask { + NSEventMaskLeftMouseDown = 1 +}; + +static const NSEventType NSFlagsChanged = NSEventTypeFlagsChanged; + +@interface NSObject +@end +@interface NSEvent : NSObject { +} ++ (nullable id) +addMonitor:(NSEventMask)mask handler:(NSEvent *_Nullable (^)(NSEvent *))block; +@end + +void test(id weakThis) { + id m_flagsChangedEventMonitor = [NSEvent + addMonitor:NSFlagsChangedMask //expected-error {{use of undeclared identifier 'NSFlagsChangedMask'}} + handler:[weakThis](NSEvent *flagsChangedEvent) { + return flagsChangedEvent; + }]; +} From 4c2f7c6a3c18b2ad93bbe7853df9508b7636c966 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 2 Feb 2016 23:58:56 +0000 Subject: [PATCH 066/742] PR24989: Stop trying to use the C++11 rules for lambda return type inference in C++14 generic lambdas. It conflicts with the C++14 return type deduction mechanism, and results in us failing to actually deduce the lambda's return type in some cases. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259609 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/SemaDecl.cpp | 32 ++++++++++++---------- lib/Sema/SemaLambda.cpp | 2 ++ test/SemaCXX/cxx1y-deduced-return-type.cpp | 6 ++++ 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 753107e09f9..7cc669c156d 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -11059,22 +11059,26 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, if (FD) { FD->setBody(Body); - if (getLangOpts().CPlusPlus14 && !FD->isInvalidDecl() && Body && - !FD->isDependentContext() && FD->getReturnType()->isUndeducedType()) { - // If the function has a deduced result type but contains no 'return' - // statements, the result type as written must be exactly 'auto', and - // the deduced result type is 'void'. - if (!FD->getReturnType()->getAs()) { - Diag(dcl->getLocation(), diag::err_auto_fn_no_return_but_not_auto) - << FD->getReturnType(); - FD->setInvalidDecl(); - } else { - // Substitute 'void' for the 'auto' in the type. - TypeLoc ResultType = getReturnTypeLoc(FD); - Context.adjustDeducedFunctionResultType( - FD, SubstAutoType(ResultType.getType(), Context.VoidTy)); + if (getLangOpts().CPlusPlus14) { + if (!FD->isInvalidDecl() && Body && !FD->isDependentContext() && + FD->getReturnType()->isUndeducedType()) { + // If the function has a deduced result type but contains no 'return' + // statements, the result type as written must be exactly 'auto', and + // the deduced result type is 'void'. + if (!FD->getReturnType()->getAs()) { + Diag(dcl->getLocation(), diag::err_auto_fn_no_return_but_not_auto) + << FD->getReturnType(); + FD->setInvalidDecl(); + } else { + // Substitute 'void' for the 'auto' in the type. + TypeLoc ResultType = getReturnTypeLoc(FD); + Context.adjustDeducedFunctionResultType( + FD, SubstAutoType(ResultType.getType(), Context.VoidTy)); + } } } else if (getLangOpts().CPlusPlus11 && isLambdaCallOperator(FD)) { + // In C++11, we don't use 'auto' deduction rules for lambda call + // operators because we don't support return type deduction. auto *LSI = getCurLambda(); if (LSI->HasImplicitReturnType) { deduceClosureReturnType(*LSI); diff --git a/lib/Sema/SemaLambda.cpp b/lib/Sema/SemaLambda.cpp index 884add26e43..1a62f0dfcba 100644 --- a/lib/Sema/SemaLambda.cpp +++ b/lib/Sema/SemaLambda.cpp @@ -617,6 +617,8 @@ void Sema::deduceClosureReturnType(CapturingScopeInfo &CSI) { assert(CSI.HasImplicitReturnType); // If it was ever a placeholder, it had to been deduced to DependentTy. assert(CSI.ReturnType.isNull() || !CSI.ReturnType->isUndeducedType()); + assert((!isa(CSI) || !getLangOpts().CPlusPlus14) && + "lambda expressions use auto deduction in C++14 onwards"); // C++ core issue 975: // If a lambda-expression does not include a trailing-return-type, diff --git a/test/SemaCXX/cxx1y-deduced-return-type.cpp b/test/SemaCXX/cxx1y-deduced-return-type.cpp index 225d2348ccd..e3f6f968017 100644 --- a/test/SemaCXX/cxx1y-deduced-return-type.cpp +++ b/test/SemaCXX/cxx1y-deduced-return-type.cpp @@ -496,3 +496,9 @@ namespace TrailingReturnTypeForConversionOperator { } }; }; + +namespace PR24989 { + auto x = [](auto){}; + using T = decltype(x); + void (T::*p)(int) const = &T::operator(); +} From fe106899f8019762c6a7ab3956b19ce51aa6d4b8 Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Wed, 27 Jan 2016 20:00:32 +0000 Subject: [PATCH 067/742] Class Property: handle class properties. At places where we handle instance properties, if necessary. rdar://23891898 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258979 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/DeclObjC.cpp | 4 ++-- lib/CodeGen/CGDebugInfo.cpp | 4 ++-- lib/Sema/SemaDeclObjC.cpp | 2 +- lib/Sema/SemaObjCProperty.cpp | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/AST/DeclObjC.cpp b/lib/AST/DeclObjC.cpp index aa012edf65c..e804fef2432 100644 --- a/lib/AST/DeclObjC.cpp +++ b/lib/AST/DeclObjC.cpp @@ -122,7 +122,7 @@ bool ObjCContainerDecl::HasUserDeclaredSetterMethod( // declaration of this property. If one found, presumably a setter will // be provided (properties declared in categories will not get // auto-synthesized). - for (const auto *P : Cat->instance_properties()) + for (const auto *P : Cat->properties()) if (P->getIdentifier() == Property->getIdentifier()) { if (P->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_readwrite) return true; @@ -1837,7 +1837,7 @@ void ObjCProtocolDecl::collectInheritedProtocolProperties( ProtocolPropertyMap &PM) const { if (const ObjCProtocolDecl *PDecl = getDefinition()) { bool MatchFound = false; - for (auto *Prop : PDecl->instance_properties()) { + for (auto *Prop : PDecl->properties()) { if (Prop == Property) continue; if (Prop->getIdentifier() == Property->getIdentifier()) { diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index 6d19e50e9f7..675b5eb07c5 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -1822,11 +1822,11 @@ llvm::DIType *CGDebugInfo::CreateTypeDefinition(const ObjCInterfaceType *Ty, { llvm::SmallPtrSet PropertySet; for (const ObjCCategoryDecl *ClassExt : ID->known_extensions()) - for (auto *PD : ClassExt->instance_properties()) { + for (auto *PD : ClassExt->properties()) { PropertySet.insert(PD->getIdentifier()); AddProperty(PD); } - for (const auto *PD : ID->instance_properties()) { + for (const auto *PD : ID->properties()) { // Don't emit duplicate metadata for properties that were already in a // class extension. if (!PropertySet.insert(PD->getIdentifier()).second) diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index ece34f26799..d4ac47fac13 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -3641,7 +3641,7 @@ Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, ArrayRef allMethods, // ProcessPropertyDecl is responsible for diagnosing conflicts with any // user-defined setter/getter. It also synthesizes setter/getter methods // and adds them to the DeclContext and global method pools. - for (auto *I : CDecl->instance_properties()) + for (auto *I : CDecl->properties()) ProcessPropertyDecl(I); CDecl->setAtEndRange(AtEnd); } diff --git a/lib/Sema/SemaObjCProperty.cpp b/lib/Sema/SemaObjCProperty.cpp index 9a976e6e590..e3889621383 100644 --- a/lib/Sema/SemaObjCProperty.cpp +++ b/lib/Sema/SemaObjCProperty.cpp @@ -1904,8 +1904,8 @@ Sema::AtomicPropertySetterGetterRules (ObjCImplDecl* IMPDecl, for (auto *Prop : Ext->instance_properties()) PM[Prop->getIdentifier()] = Prop; - for (ObjCContainerDecl::PropertyMap::iterator I = PM.begin(), E = PM.end(); - I != E; ++I) { + for (ObjCContainerDecl::PropertyMap::iterator I = PM.begin(), E = PM.end(); + I != E; ++I) { const ObjCPropertyDecl *Property = I->second; ObjCMethodDecl *GetterMethod = nullptr; ObjCMethodDecl *SetterMethod = nullptr; From 0faa3f83f1156a651de5d70ceb8f2a895b59da1c Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Wed, 27 Jan 2016 20:10:32 +0000 Subject: [PATCH 068/742] Class Property: create accessors (class methods) for class property. Change a few places where we assume property accessors can only be instance methods. rdar://23891898 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258980 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/DeclObjC.h | 5 ++- lib/Sema/SemaDeclObjC.cpp | 17 +++++---- lib/Sema/SemaObjCProperty.cpp | 57 ++++++++++++++++++++--------- test/SemaObjC/objc-class-property.m | 30 +++++++++++++++ 4 files changed, 83 insertions(+), 26 deletions(-) create mode 100644 test/SemaObjC/objc-class-property.m diff --git a/include/clang/AST/DeclObjC.h b/include/clang/AST/DeclObjC.h index 18f89e16acb..f9f47cd3db0 100644 --- a/include/clang/AST/DeclObjC.h +++ b/include/clang/AST/DeclObjC.h @@ -1754,8 +1754,9 @@ class ObjCInterfaceDecl : public ObjCContainerDecl /// including in all categories except for category passed /// as argument. ObjCMethodDecl *lookupPropertyAccessor(const Selector Sel, - const ObjCCategoryDecl *Cat) const { - return lookupMethod(Sel, true/*isInstance*/, + const ObjCCategoryDecl *Cat, + bool IsClassProperty) const { + return lookupMethod(Sel, !IsClassProperty/*isInstance*/, false/*shallowCategoryLookup*/, true /* followsSuper */, Cat); diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index d4ac47fac13..2c2ef43b974 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -2738,7 +2738,8 @@ void Sema::MatchAllMethodDeclarations(const SelectorSet &InsMap, for (auto *I : CDecl->class_methods()) { if (!ClsMapSeen.insert(I->getSelector()).second) continue; - if (!ClsMap.count(I->getSelector())) { + if (!I->isPropertyAccessor() && + !ClsMap.count(I->getSelector())) { if (ImmediateClass) WarnUndefinedMethod(*this, IMPDecl->getLocation(), I, IncompleteImpl, diag::warn_undef_method_impl); @@ -2747,12 +2748,14 @@ void Sema::MatchAllMethodDeclarations(const SelectorSet &InsMap, IMPDecl->getClassMethod(I->getSelector()); assert(CDecl->getClassMethod(I->getSelector()) && "Expected to find the method through lookup as well"); - if (!WarnCategoryMethodImpl) - WarnConflictingTypedMethods(ImpMethodDecl, I, - isa(CDecl)); - else - WarnExactTypedMethods(ImpMethodDecl, I, - isa(CDecl)); + // ImpMethodDecl may be null as in a @dynamic property. + if (ImpMethodDecl) { + if (!WarnCategoryMethodImpl) + WarnConflictingTypedMethods(ImpMethodDecl, I, + isa(CDecl)); + else if (!I->isPropertyAccessor()) + WarnExactTypedMethods(ImpMethodDecl, I, isa(CDecl)); + } } } diff --git a/lib/Sema/SemaObjCProperty.cpp b/lib/Sema/SemaObjCProperty.cpp index e3889621383..4c5a0616f33 100644 --- a/lib/Sema/SemaObjCProperty.cpp +++ b/lib/Sema/SemaObjCProperty.cpp @@ -1745,7 +1745,8 @@ static void DiagnoseUnimplementedAccessor(Sema &S, // the class is going to implement them. if (!SMap.count(Method) && (PrimaryClass == nullptr || - !PrimaryClass->lookupPropertyAccessor(Method, C))) { + !PrimaryClass->lookupPropertyAccessor(Method, C, + Prop->isClassProperty()))) { S.Diag(IMPDecl->getLocation(), isa(CDecl) ? diag::warn_setter_getter_impl_required_in_category : @@ -1916,8 +1917,12 @@ Sema::AtomicPropertySetterGetterRules (ObjCImplDecl* IMPDecl, if (!(AttributesAsWritten & ObjCPropertyDecl::OBJC_PR_atomic) && !(AttributesAsWritten & ObjCPropertyDecl::OBJC_PR_nonatomic)) { - GetterMethod = IMPDecl->getInstanceMethod(Property->getGetterName()); - SetterMethod = IMPDecl->getInstanceMethod(Property->getSetterName()); + GetterMethod = Property->isClassProperty() ? + IMPDecl->getClassMethod(Property->getGetterName()) : + IMPDecl->getInstanceMethod(Property->getGetterName()); + SetterMethod = Property->isClassProperty() ? + IMPDecl->getClassMethod(Property->getSetterName()) : + IMPDecl->getInstanceMethod(Property->getSetterName()); LookedUpGetterSetter = true; if (GetterMethod) { Diag(GetterMethod->getLocation(), @@ -1942,8 +1947,12 @@ Sema::AtomicPropertySetterGetterRules (ObjCImplDecl* IMPDecl, if (PIDecl->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) continue; if (!LookedUpGetterSetter) { - GetterMethod = IMPDecl->getInstanceMethod(Property->getGetterName()); - SetterMethod = IMPDecl->getInstanceMethod(Property->getSetterName()); + GetterMethod = Property->isClassProperty() ? + IMPDecl->getClassMethod(Property->getGetterName()) : + IMPDecl->getInstanceMethod(Property->getGetterName()); + SetterMethod = Property->isClassProperty() ? + IMPDecl->getClassMethod(Property->getSetterName()) : + IMPDecl->getInstanceMethod(Property->getSetterName()); } if ((GetterMethod && !SetterMethod) || (!GetterMethod && SetterMethod)) { SourceLocation MethodLoc = @@ -1986,6 +1995,7 @@ void Sema::DiagnoseOwningPropertyGetterSynthesis(const ObjCImplementationDecl *D for (const auto *PID : D->property_impls()) { const ObjCPropertyDecl *PD = PID->getPropertyDecl(); if (PD && !PD->hasAttr() && + !PD->isClassProperty() && !D->getInstanceMethod(PD->getGetterName())) { ObjCMethodDecl *method = PD->getGetterMethodDecl(); if (!method) @@ -2091,20 +2101,30 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property) { if (CD->isInvalidDecl()) return; - GetterMethod = CD->getInstanceMethod(property->getGetterName()); + bool IsClassProperty = property->isClassProperty(); + GetterMethod = IsClassProperty ? + CD->getClassMethod(property->getGetterName()) : + CD->getInstanceMethod(property->getGetterName()); + // if setter or getter is not found in class extension, it might be // in the primary class. if (!GetterMethod) if (const ObjCCategoryDecl *CatDecl = dyn_cast(CD)) if (CatDecl->IsClassExtension()) - GetterMethod = CatDecl->getClassInterface()-> + GetterMethod = IsClassProperty ? CatDecl->getClassInterface()-> + getClassMethod(property->getGetterName()) : + CatDecl->getClassInterface()-> getInstanceMethod(property->getGetterName()); - SetterMethod = CD->getInstanceMethod(property->getSetterName()); + SetterMethod = IsClassProperty ? + CD->getClassMethod(property->getSetterName()) : + CD->getInstanceMethod(property->getSetterName()); if (!SetterMethod) if (const ObjCCategoryDecl *CatDecl = dyn_cast(CD)) if (CatDecl->IsClassExtension()) - SetterMethod = CatDecl->getClassInterface()-> + SetterMethod = IsClassProperty ? CatDecl->getClassInterface()-> + getClassMethod(property->getSetterName()) : + CatDecl->getClassInterface()-> getInstanceMethod(property->getSetterName()); DiagnosePropertyAccessorMismatch(property, GetterMethod, property->getLocation()); @@ -2135,7 +2155,7 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property) { // (which is odd, but allowed). Sema should be typechecking that the // declarations jive in that situation (which it is not currently). if (!GetterMethod) { - // No instance method of same name as property getter name was found. + // No instance/class method of same name as property getter name was found. // Declare a getter method and add it to the list of methods // for this class. SourceLocation Loc = property->getLocation(); @@ -2155,7 +2175,7 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property) { GetterMethod = ObjCMethodDecl::Create(Context, Loc, Loc, property->getGetterName(), resultTy, nullptr, CD, - /*isInstance=*/true, /*isVariadic=*/false, + !IsClassProperty, /*isVariadic=*/false, /*isPropertyAccessor=*/true, /*isImplicitlyDeclared=*/true, /*isDefined=*/false, (property->getPropertyImplementation() == @@ -2191,7 +2211,8 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property) { if (!property->isReadOnly()) { // Find the default setter and if one not found, add one. if (!SetterMethod) { - // No instance method of same name as property setter name was found. + // No instance/class method of same name as property setter name was + // found. // Declare a setter method and add it to the list of methods // for this class. SourceLocation Loc = property->getLocation(); @@ -2199,7 +2220,7 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property) { SetterMethod = ObjCMethodDecl::Create(Context, Loc, Loc, property->getSetterName(), Context.VoidTy, - nullptr, CD, /*isInstance=*/true, + nullptr, CD, !IsClassProperty, /*isVariadic=*/false, /*isPropertyAccessor=*/true, /*isImplicitlyDeclared=*/true, @@ -2262,10 +2283,12 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property) { // double bar = [foo bar]; // } // - if (GetterMethod) - AddInstanceMethodToGlobalPool(GetterMethod); - if (SetterMethod) - AddInstanceMethodToGlobalPool(SetterMethod); + if (!IsClassProperty) { + if (GetterMethod) + AddInstanceMethodToGlobalPool(GetterMethod); + if (SetterMethod) + AddInstanceMethodToGlobalPool(SetterMethod); + } ObjCInterfaceDecl *CurrentClass = dyn_cast(CD); if (!CurrentClass) { diff --git a/test/SemaObjC/objc-class-property.m b/test/SemaObjC/objc-class-property.m new file mode 100644 index 00000000000..f24e86b6355 --- /dev/null +++ b/test/SemaObjC/objc-class-property.m @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +// expected-no-diagnostics + +@interface Root +-(id) alloc; +-(id) init; +@end + +@interface A : Root { + int x; + int z; +} +@property int x; +@property int y; +@property int z; +@property(readonly) int ro, ro2; +@property (class) int c; +@property (class) int c2; +@end + +@implementation A +@dynamic x; +@synthesize z; +@dynamic c; +@end + +int test() { + A *a = [[A alloc] init]; + return a.x + A.c; +} From 11e6ee6da0718b2d4fb9898a6485d060d043617f Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Thu, 28 Jan 2016 18:49:28 +0000 Subject: [PATCH 069/742] Class Property: class property and instance property can have the same name. Add "enum ObjCPropertyQueryKind" to a few APIs that used to only take the name of the property: ObjCPropertyDecl::findPropertyDecl, ObjCContainerDecl::FindPropertyDeclaration, ObjCInterfaceDecl::FindPropertyVisibleInPrimaryClass, ObjCImplDecl::FindPropertyImplDecl, and Sema::ActOnPropertyImplDecl. ObjCPropertyQueryKind currently has 3 values: OBJC_PR_query_unknown, OBJC_PR_query_instance, OBJC_PR_query_class This extra parameter specifies that we are looking for an instance property with the given name, or a class property with the given name, or any property with the given name (if both exist, the instance property will be returned). rdar://23891898 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259070 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/DeclObjC.h | 26 +++++++-- include/clang/Sema/Sema.h | 3 +- lib/ARCMigrate/ObjCMT.cpp | 3 +- lib/AST/ASTImporter.cpp | 3 +- lib/AST/DeclObjC.cpp | 83 ++++++++++++++++++++++------- lib/Analysis/BodyFarm.cpp | 2 +- lib/Parse/ParseObjc.cpp | 12 +++-- lib/Sema/SemaCodeComplete.cpp | 6 +-- lib/Sema/SemaDeclObjC.cpp | 3 +- lib/Sema/SemaExprMember.cpp | 10 ++-- lib/Sema/SemaExprObjC.cpp | 11 ++-- lib/Sema/SemaLookup.cpp | 3 +- lib/Sema/SemaObjCProperty.cpp | 43 +++++++++------ lib/Sema/SemaPseudoObject.cpp | 3 +- test/SemaObjC/objc-class-property.m | 1 + tools/libclang/CIndex.cpp | 3 +- 16 files changed, 154 insertions(+), 61 deletions(-) diff --git a/include/clang/AST/DeclObjC.h b/include/clang/AST/DeclObjC.h index f9f47cd3db0..def08daf7f3 100644 --- a/include/clang/AST/DeclObjC.h +++ b/include/clang/AST/DeclObjC.h @@ -689,6 +689,12 @@ class ObjCTypeParamList final friend TrailingObjects; }; +enum class ObjCPropertyQueryKind : uint8_t { + OBJC_PR_query_unknown = 0x00, + OBJC_PR_query_instance, + OBJC_PR_query_class +}; + /// \brief Represents one property declaration in an Objective-C interface. /// /// For example: @@ -826,6 +832,14 @@ class ObjCPropertyDecl : public NamedDecl { bool isInstanceProperty() const { return !isClassProperty(); } bool isClassProperty() const { return PropertyAttributes & OBJC_PR_class; } + ObjCPropertyQueryKind getQueryKind() const { + return isClassProperty() ? ObjCPropertyQueryKind::OBJC_PR_query_class : + ObjCPropertyQueryKind::OBJC_PR_query_instance; + } + static ObjCPropertyQueryKind getQueryKind(bool isClassProperty) { + return isClassProperty ? ObjCPropertyQueryKind::OBJC_PR_query_class : + ObjCPropertyQueryKind::OBJC_PR_query_instance; + } /// getSetterKind - Return the method used for doing assignment in /// the property setter. This is only valid if the property has been @@ -878,7 +892,8 @@ class ObjCPropertyDecl : public NamedDecl { /// Lookup a property by name in the specified DeclContext. static ObjCPropertyDecl *findPropertyDecl(const DeclContext *DC, - const IdentifierInfo *propertyID); + const IdentifierInfo *propertyID, + ObjCPropertyQueryKind queryKind); static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classofKind(Kind K) { return K == ObjCProperty; } @@ -1005,7 +1020,8 @@ class ObjCContainerDecl : public NamedDecl, public DeclContext { ObjCIvarDecl *getIvarDecl(IdentifierInfo *Id) const; ObjCPropertyDecl * - FindPropertyDeclaration(const IdentifierInfo *PropertyId) const; + FindPropertyDeclaration(const IdentifierInfo *PropertyId, + ObjCPropertyQueryKind QueryKind) const; typedef llvm::DenseMap PropertyMap; @@ -1688,7 +1704,8 @@ class ObjCInterfaceDecl : public ObjCContainerDecl } ObjCPropertyDecl - *FindPropertyVisibleInPrimaryClass(IdentifierInfo *PropertyId) const; + *FindPropertyVisibleInPrimaryClass(IdentifierInfo *PropertyId, + ObjCPropertyQueryKind QueryKind) const; void collectPropertiesToImplement(PropertyMap &PM, PropertyDeclOrder &PO) const override; @@ -2325,7 +2342,8 @@ class ObjCImplDecl : public ObjCContainerDecl { void addPropertyImplementation(ObjCPropertyImplDecl *property); - ObjCPropertyImplDecl *FindPropertyImplDecl(IdentifierInfo *propertyId) const; + ObjCPropertyImplDecl *FindPropertyImplDecl(IdentifierInfo *propertyId, + ObjCPropertyQueryKind queryKind) const; ObjCPropertyImplDecl *FindPropertyImplIvarDecl(IdentifierInfo *ivarId) const; // Iterator access to properties. diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index abc5ac71949..ebdffc6f19f 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -7363,7 +7363,8 @@ class Sema { bool ImplKind, IdentifierInfo *PropertyId, IdentifierInfo *PropertyIvar, - SourceLocation PropertyIvarLoc); + SourceLocation PropertyIvarLoc, + ObjCPropertyQueryKind QueryKind); enum ObjCSpecialMethodKind { OSMK_None, diff --git a/lib/ARCMigrate/ObjCMT.cpp b/lib/ARCMigrate/ObjCMT.cpp index 10996e83208..1be724c38af 100644 --- a/lib/ARCMigrate/ObjCMT.cpp +++ b/lib/ARCMigrate/ObjCMT.cpp @@ -615,7 +615,8 @@ ClassImplementsAllMethodsAndProperties(ASTContext &Ctx, // or dynamic declaration. Class is implementing a property coming from // another protocol. This still makes the target protocol as conforming. if (!ImpDecl->FindPropertyImplDecl( - Property->getDeclName().getAsIdentifierInfo())) + Property->getDeclName().getAsIdentifierInfo(), + Property->getQueryKind())) return false; } else if (ObjCPropertyDecl *ClassProperty = dyn_cast(R[0])) { diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 916f1081798..018fafff9c6 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -4061,7 +4061,8 @@ Decl *ASTNodeImporter::VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D) { } ObjCPropertyImplDecl *ToImpl - = InImpl->FindPropertyImplDecl(Property->getIdentifier()); + = InImpl->FindPropertyImplDecl(Property->getIdentifier(), + Property->getQueryKind()); if (!ToImpl) { ToImpl = ObjCPropertyImplDecl::Create(Importer.getToContext(), DC, Importer.Import(D->getLocStart()), diff --git a/lib/AST/DeclObjC.cpp b/lib/AST/DeclObjC.cpp index e804fef2432..230c2cfd50e 100644 --- a/lib/AST/DeclObjC.cpp +++ b/lib/AST/DeclObjC.cpp @@ -152,7 +152,8 @@ bool ObjCContainerDecl::HasUserDeclaredSetterMethod( ObjCPropertyDecl * ObjCPropertyDecl::findPropertyDecl(const DeclContext *DC, - const IdentifierInfo *propertyID) { + const IdentifierInfo *propertyID, + ObjCPropertyQueryKind queryKind) { // If this context is a hidden protocol definition, don't find any // property. if (const ObjCProtocolDecl *Proto = dyn_cast(DC)) { @@ -166,15 +167,33 @@ ObjCPropertyDecl::findPropertyDecl(const DeclContext *DC, if (auto *IDecl = dyn_cast(DC)) { for (const auto *Ext : IDecl->known_extensions()) if (ObjCPropertyDecl *PD = ObjCPropertyDecl::findPropertyDecl(Ext, - propertyID)) + propertyID, + queryKind)) return PD; } DeclContext::lookup_result R = DC->lookup(propertyID); + ObjCPropertyDecl *classProp = nullptr; for (DeclContext::lookup_iterator I = R.begin(), E = R.end(); I != E; ++I) - if (ObjCPropertyDecl *PD = dyn_cast(*I)) - return PD; + if (ObjCPropertyDecl *PD = dyn_cast(*I)) { + // If queryKind is unknown, we return the instance property if one + // exists; otherwise we return the class property. + if ((queryKind == ObjCPropertyQueryKind::OBJC_PR_query_unknown && + !PD->isClassProperty()) || + (queryKind == ObjCPropertyQueryKind::OBJC_PR_query_class && + PD->isClassProperty()) || + (queryKind == ObjCPropertyQueryKind::OBJC_PR_query_instance && + !PD->isClassProperty())) + return PD; + + if (PD->isClassProperty()) + classProp = PD; + } + + if (queryKind == ObjCPropertyQueryKind::OBJC_PR_query_unknown) + // We can't find the instance property, return the class property. + return classProp; return nullptr; } @@ -192,7 +211,8 @@ ObjCPropertyDecl::getDefaultSynthIvarName(ASTContext &Ctx) const { /// FindPropertyDeclaration - Finds declaration of the property given its name /// in 'PropertyId' and returns it. It returns 0, if not found. ObjCPropertyDecl *ObjCContainerDecl::FindPropertyDeclaration( - const IdentifierInfo *PropertyId) const { + const IdentifierInfo *PropertyId, + ObjCPropertyQueryKind QueryKind) const { // Don't find properties within hidden protocol definitions. if (const ObjCProtocolDecl *Proto = dyn_cast(this)) { if (const ObjCProtocolDecl *Def = Proto->getDefinition()) @@ -204,13 +224,14 @@ ObjCPropertyDecl *ObjCContainerDecl::FindPropertyDeclaration( // the class itself. if (const auto *ClassDecl = dyn_cast(this)) { for (const auto *Ext : ClassDecl->visible_extensions()) { - if (auto *P = Ext->FindPropertyDeclaration(PropertyId)) + if (auto *P = Ext->FindPropertyDeclaration(PropertyId, QueryKind)) return P; } } if (ObjCPropertyDecl *PD = - ObjCPropertyDecl::findPropertyDecl(cast(this), PropertyId)) + ObjCPropertyDecl::findPropertyDecl(cast(this), PropertyId, + QueryKind)) return PD; switch (getKind()) { @@ -219,7 +240,8 @@ ObjCPropertyDecl *ObjCContainerDecl::FindPropertyDeclaration( case Decl::ObjCProtocol: { const ObjCProtocolDecl *PID = cast(this); for (const auto *I : PID->protocols()) - if (ObjCPropertyDecl *P = I->FindPropertyDeclaration(PropertyId)) + if (ObjCPropertyDecl *P = I->FindPropertyDeclaration(PropertyId, + QueryKind)) return P; break; } @@ -228,18 +250,20 @@ ObjCPropertyDecl *ObjCContainerDecl::FindPropertyDeclaration( // Look through categories (but not extensions; they were handled above). for (const auto *Cat : OID->visible_categories()) { if (!Cat->IsClassExtension()) - if (ObjCPropertyDecl *P = Cat->FindPropertyDeclaration(PropertyId)) + if (ObjCPropertyDecl *P = Cat->FindPropertyDeclaration( + PropertyId, QueryKind)) return P; } // Look through protocols. for (const auto *I : OID->all_referenced_protocols()) - if (ObjCPropertyDecl *P = I->FindPropertyDeclaration(PropertyId)) + if (ObjCPropertyDecl *P = I->FindPropertyDeclaration(PropertyId, + QueryKind)) return P; // Finally, check the super class. if (const ObjCInterfaceDecl *superClass = OID->getSuperClass()) - return superClass->FindPropertyDeclaration(PropertyId); + return superClass->FindPropertyDeclaration(PropertyId, QueryKind); break; } case Decl::ObjCCategory: { @@ -247,7 +271,8 @@ ObjCPropertyDecl *ObjCContainerDecl::FindPropertyDeclaration( // Look through protocols. if (!OCD->IsClassExtension()) for (const auto *I : OCD->protocols()) - if (ObjCPropertyDecl *P = I->FindPropertyDeclaration(PropertyId)) + if (ObjCPropertyDecl *P = I->FindPropertyDeclaration(PropertyId, + QueryKind)) return P; break; } @@ -319,7 +344,8 @@ SourceLocation ObjCInterfaceDecl::getSuperClassLoc() const { /// ObjCPropertyDecl * ObjCInterfaceDecl::FindPropertyVisibleInPrimaryClass( - IdentifierInfo *PropertyId) const { + IdentifierInfo *PropertyId, + ObjCPropertyQueryKind QueryKind) const { // FIXME: Should make sure no callers ever do this. if (!hasDefinition()) return nullptr; @@ -328,12 +354,14 @@ ObjCInterfaceDecl::FindPropertyVisibleInPrimaryClass( LoadExternalDefinition(); if (ObjCPropertyDecl *PD = - ObjCPropertyDecl::findPropertyDecl(cast(this), PropertyId)) + ObjCPropertyDecl::findPropertyDecl(cast(this), PropertyId, + QueryKind)) return PD; // Look through protocols. for (const auto *I : all_referenced_protocols()) - if (ObjCPropertyDecl *P = I->FindPropertyDeclaration(PropertyId)) + if (ObjCPropertyDecl *P = I->FindPropertyDeclaration(PropertyId, + QueryKind)) return P; return nullptr; @@ -2011,10 +2039,29 @@ FindPropertyImplIvarDecl(IdentifierInfo *ivarId) const { /// category \@implementation block. /// ObjCPropertyImplDecl *ObjCImplDecl:: -FindPropertyImplDecl(IdentifierInfo *Id) const { +FindPropertyImplDecl(IdentifierInfo *Id, + ObjCPropertyQueryKind QueryKind) const { + ObjCPropertyImplDecl *ClassPropImpl = nullptr; for (auto *PID : property_impls()) - if (PID->getPropertyDecl()->getIdentifier() == Id) - return PID; + // If queryKind is unknown, we return the instance property if one + // exists; otherwise we return the class property. + if (PID->getPropertyDecl()->getIdentifier() == Id) { + if ((QueryKind == ObjCPropertyQueryKind::OBJC_PR_query_unknown && + !PID->getPropertyDecl()->isClassProperty()) || + (QueryKind == ObjCPropertyQueryKind::OBJC_PR_query_class && + PID->getPropertyDecl()->isClassProperty()) || + (QueryKind == ObjCPropertyQueryKind::OBJC_PR_query_instance && + !PID->getPropertyDecl()->isClassProperty())) + return PID; + + if (PID->getPropertyDecl()->isClassProperty()) + ClassPropImpl = PID; + } + + if (QueryKind == ObjCPropertyQueryKind::OBJC_PR_query_unknown) + // We can't find the instance property, return the class property. + return ClassPropImpl; + return nullptr; } diff --git a/lib/Analysis/BodyFarm.cpp b/lib/Analysis/BodyFarm.cpp index 325c3261374..2118301362e 100644 --- a/lib/Analysis/BodyFarm.cpp +++ b/lib/Analysis/BodyFarm.cpp @@ -414,7 +414,7 @@ static const ObjCIvarDecl *findBackingIvar(const ObjCPropertyDecl *Prop) { // is guaranteed to find the shadowing property, if it exists, rather than // the shadowed property. auto *ShadowingProp = PrimaryInterface->FindPropertyVisibleInPrimaryClass( - Prop->getIdentifier()); + Prop->getIdentifier(), Prop->getQueryKind()); if (ShadowingProp && ShadowingProp != Prop) { IVar = ShadowingProp->getPropertyIvarDecl(); } diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index 33c422b6d6f..11e9204df0b 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/lib/Parse/ParseObjc.cpp @@ -2332,8 +2332,10 @@ Decl *Parser::ParseObjCPropertySynthesize(SourceLocation atLoc) { propertyIvar = Tok.getIdentifierInfo(); propertyIvarLoc = ConsumeToken(); // consume ivar-name } - Actions.ActOnPropertyImplDecl(getCurScope(), atLoc, propertyLoc, true, - propertyId, propertyIvar, propertyIvarLoc); + Actions.ActOnPropertyImplDecl( + getCurScope(), atLoc, propertyLoc, true, + propertyId, propertyIvar, propertyIvarLoc, + ObjCPropertyQueryKind::OBJC_PR_query_unknown); if (Tok.isNot(tok::comma)) break; ConsumeToken(); // consume ',' @@ -2368,8 +2370,10 @@ Decl *Parser::ParseObjCPropertyDynamic(SourceLocation atLoc) { IdentifierInfo *propertyId = Tok.getIdentifierInfo(); SourceLocation propertyLoc = ConsumeToken(); // consume property name - Actions.ActOnPropertyImplDecl(getCurScope(), atLoc, propertyLoc, false, - propertyId, nullptr, SourceLocation()); + Actions.ActOnPropertyImplDecl( + getCurScope(), atLoc, propertyLoc, false, + propertyId, nullptr, SourceLocation(), + ObjCPropertyQueryKind::OBJC_PR_query_unknown); if (Tok.isNot(tok::comma)) break; diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index 59ab0ba618d..12aec6caba9 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -6189,7 +6189,7 @@ void Sema::CodeCompleteObjCPropertySynthesizeIvar(Scope *S, // Figure out which interface we're looking into. ObjCInterfaceDecl *Class = nullptr; if (ObjCImplementationDecl *ClassImpl - = dyn_cast(Container)) + = dyn_cast(Container)) Class = ClassImpl->getClassInterface(); else Class = cast(Container)->getCategoryDecl() @@ -6198,8 +6198,8 @@ void Sema::CodeCompleteObjCPropertySynthesizeIvar(Scope *S, // Determine the type of the property we're synthesizing. QualType PropertyType = Context.getObjCIdType(); if (Class) { - if (ObjCPropertyDecl *Property - = Class->FindPropertyDeclaration(PropertyName)) { + if (ObjCPropertyDecl *Property = Class->FindPropertyDeclaration( + PropertyName, ObjCPropertyQueryKind::OBJC_PR_query_instance)) { PropertyType = Property->getType().getNonReferenceType().getUnqualifiedType(); diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index 2c2ef43b974..3f957af489e 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -3660,7 +3660,8 @@ Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, ArrayRef allMethods, for (const auto *Property : Ext->instance_properties()) { // Skip over properties declared @dynamic if (const ObjCPropertyImplDecl *PIDecl - = IC->FindPropertyImplDecl(Property->getIdentifier())) + = IC->FindPropertyImplDecl(Property->getIdentifier(), + Property->getQueryKind())) if (PIDecl->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) continue; diff --git a/lib/Sema/SemaExprMember.cpp b/lib/Sema/SemaExprMember.cpp index 9c345f8a69a..f62b5a58e84 100644 --- a/lib/Sema/SemaExprMember.cpp +++ b/lib/Sema/SemaExprMember.cpp @@ -380,7 +380,8 @@ static Decl *FindGetterSetterNameDeclFromProtocolList(const ObjCProtocolDecl*PDe const Selector &Sel, ASTContext &Context) { if (Member) - if (ObjCPropertyDecl *PD = PDecl->FindPropertyDeclaration(Member)) + if (ObjCPropertyDecl *PD = PDecl->FindPropertyDeclaration( + Member, ObjCPropertyQueryKind::OBJC_PR_query_instance)) return PD; if (ObjCMethodDecl *OMD = PDecl->getInstanceMethod(Sel)) return OMD; @@ -401,7 +402,8 @@ static Decl *FindGetterSetterNameDecl(const ObjCObjectPointerType *QIdTy, Decl *GDecl = nullptr; for (const auto *I : QIdTy->quals()) { if (Member) - if (ObjCPropertyDecl *PD = I->FindPropertyDeclaration(Member)) { + if (ObjCPropertyDecl *PD = I->FindPropertyDeclaration( + Member, ObjCPropertyQueryKind::OBJC_PR_query_instance)) { GDecl = PD; break; } @@ -1324,7 +1326,9 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, D = CAT->getClassInterface(); ClassDeclared = cast(D); } else { - if (IsArrow && IDecl->FindPropertyDeclaration(Member)) { + if (IsArrow && + IDecl->FindPropertyDeclaration( + Member, ObjCPropertyQueryKind::OBJC_PR_query_instance)) { S.Diag(MemberLoc, diag::err_property_found_suggest) << Member << BaseExpr.get()->getType() << FixItHint::CreateReplacement(OpLoc, "."); diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp index b14ca4b64b6..53f39e3b2a3 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/lib/Sema/SemaExprObjC.cpp @@ -1778,7 +1778,8 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT, MemberName, BaseRange)) return ExprError(); - if (ObjCPropertyDecl *PD = IFace->FindPropertyDeclaration(Member)) { + if (ObjCPropertyDecl *PD = IFace->FindPropertyDeclaration( + Member, ObjCPropertyQueryKind::OBJC_PR_query_instance)) { // Check whether we can reference this property. if (DiagnoseUseOfDecl(PD, MemberLoc)) return ExprError(); @@ -1793,7 +1794,8 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT, } // Check protocols on qualified interfaces. for (const auto *I : OPT->quals()) - if (ObjCPropertyDecl *PD = I->FindPropertyDeclaration(Member)) { + if (ObjCPropertyDecl *PD = I->FindPropertyDeclaration( + Member, ObjCPropertyQueryKind::OBJC_PR_query_instance)) { // Check whether we can reference this property. if (DiagnoseUseOfDecl(PD, MemberLoc)) return ExprError(); @@ -1852,8 +1854,9 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT, // Special warning if member name used in a property-dot for a setter accessor // does not use a property with same name; e.g. obj.X = ... for a property with // name 'x'. - if (Setter && Setter->isImplicit() && Setter->isPropertyAccessor() - && !IFace->FindPropertyDeclaration(Member)) { + if (Setter && Setter->isImplicit() && Setter->isPropertyAccessor() && + !IFace->FindPropertyDeclaration( + Member, ObjCPropertyQueryKind::OBJC_PR_query_instance)) { if (const ObjCPropertyDecl *PDecl = Setter->findPropertyDecl()) { // Do not warn if user is using property-dot syntax to make call to // user named setter. diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index 45dc2e33da9..f28cd27e85c 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -4207,7 +4207,8 @@ static void LookupPotentialTypoResult(Sema &SemaRef, } } - if (ObjCPropertyDecl *Prop = Class->FindPropertyDeclaration(Name)) { + if (ObjCPropertyDecl *Prop = Class->FindPropertyDeclaration( + Name, ObjCPropertyQueryKind::OBJC_PR_query_instance)) { Res.addDecl(Prop); Res.resolveKind(); return; diff --git a/lib/Sema/SemaObjCProperty.cpp b/lib/Sema/SemaObjCProperty.cpp index 4c5a0616f33..35f79b2a3e3 100644 --- a/lib/Sema/SemaObjCProperty.cpp +++ b/lib/Sema/SemaObjCProperty.cpp @@ -433,10 +433,13 @@ Sema::HandlePropertyInClassExtension(Scope *S, return nullptr; } + bool isClassProperty = (AttributesAsWritten & ObjCDeclSpec::DQ_PR_class) || + (Attributes & ObjCDeclSpec::DQ_PR_class); + // Find the property in the extended class's primary class or // extensions. - ObjCPropertyDecl *PIDecl = - CCPrimary->FindPropertyVisibleInPrimaryClass(PropertyId); + ObjCPropertyDecl *PIDecl = CCPrimary->FindPropertyVisibleInPrimaryClass( + PropertyId, ObjCPropertyDecl::getQueryKind(isClassProperty)); // If we found a property in an extension, complain. if (PIDecl && isa(PIDecl->getDeclContext())) { @@ -614,8 +617,11 @@ ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S, PropertyId, AtLoc, LParenLoc, T, TInfo); - if (ObjCPropertyDecl *prevDecl = - ObjCPropertyDecl::findPropertyDecl(DC, PropertyId)) { + bool isClassProperty = (AttributesAsWritten & ObjCDeclSpec::DQ_PR_class) || + (Attributes & ObjCDeclSpec::DQ_PR_class); + // Class property and instance property can have the same name. + if (ObjCPropertyDecl *prevDecl = ObjCPropertyDecl::findPropertyDecl( + DC, PropertyId, ObjCPropertyDecl::getQueryKind(isClassProperty))) { Diag(PDecl->getLocation(), diag::err_duplicate_property); Diag(prevDecl->getLocation(), diag::note_property_declare); PDecl->setInvalidDecl(); @@ -852,7 +858,8 @@ DiagnosePropertyMismatchDeclInProtocols(Sema &S, SourceLocation AtLoc, } /// Determine whether any storage attributes were written on the property. -static bool hasWrittenStorageAttribute(ObjCPropertyDecl *Prop) { +static bool hasWrittenStorageAttribute(ObjCPropertyDecl *Prop, + ObjCPropertyQueryKind QueryKind) { if (Prop->getPropertyAttributesAsWritten() & OwnershipMask) return true; // If this is a readwrite property in a class extension that refines @@ -875,8 +882,8 @@ static bool hasWrittenStorageAttribute(ObjCPropertyDecl *Prop) { // Look through all of the protocols. for (const auto *Proto : OrigClass->all_referenced_protocols()) { - if (ObjCPropertyDecl *OrigProp = - Proto->FindPropertyDeclaration(Prop->getIdentifier())) + if (ObjCPropertyDecl *OrigProp = Proto->FindPropertyDeclaration( + Prop->getIdentifier(), QueryKind)) return OrigProp->getPropertyAttributesAsWritten() & OwnershipMask; } @@ -893,7 +900,8 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, bool Synthesize, IdentifierInfo *PropertyId, IdentifierInfo *PropertyIvar, - SourceLocation PropertyIvarLoc) { + SourceLocation PropertyIvarLoc, + ObjCPropertyQueryKind QueryKind) { ObjCContainerDecl *ClassImpDecl = dyn_cast(CurContext); // Make sure we have a context for the property implementation declaration. @@ -920,7 +928,7 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, "ActOnPropertyImplDecl - @implementation without @interface"); // Look for this property declaration in the @implementation's @interface - property = IDecl->FindPropertyDeclaration(PropertyId); + property = IDecl->FindPropertyDeclaration(PropertyId, QueryKind); if (!property) { Diag(PropertyLoc, diag::error_bad_property_decl) << IDecl->getDeclName(); return nullptr; @@ -998,7 +1006,7 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, if (!Category) return nullptr; // Look for this property declaration in @implementation's category - property = Category->FindPropertyDeclaration(PropertyId); + property = Category->FindPropertyDeclaration(PropertyId, QueryKind); if (!property) { Diag(PropertyLoc, diag::error_bad_category_property_decl) << Category->getDeclName(); @@ -1110,7 +1118,7 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, // It's an error if we have to do this and the user didn't // explicitly write an ownership attribute on the property. - if (!hasWrittenStorageAttribute(property) && + if (!hasWrittenStorageAttribute(property, QueryKind) && !(kind & ObjCPropertyDecl::OBJC_PR_strong)) { Diag(PropertyDiagLoc, diag::err_arc_objc_property_default_assign_on_object); @@ -1345,7 +1353,7 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, } if (ObjCPropertyImplDecl *PPIDecl - = IC->FindPropertyImplDecl(PropertyId)) { + = IC->FindPropertyImplDecl(PropertyId, QueryKind)) { Diag(PropertyLoc, diag::error_property_implemented) << PropertyId; Diag(PPIDecl->getLocation(), diag::note_previous_declaration); return nullptr; @@ -1384,7 +1392,7 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, } if (ObjCPropertyImplDecl *PPIDecl = - CatImplClass->FindPropertyImplDecl(PropertyId)) { + CatImplClass->FindPropertyImplDecl(PropertyId, QueryKind)) { Diag(PropertyDiagLoc, diag::error_property_implemented) << PropertyId; Diag(PPIDecl->getLocation(), diag::note_previous_declaration); return nullptr; @@ -1653,7 +1661,8 @@ void Sema::DefaultSynthesizeProperties(Scope *S, ObjCImplDecl* IMPDecl, Prop->getPropertyImplementation() == ObjCPropertyDecl::Optional) continue; // Property may have been synthesized by user. - if (IMPDecl->FindPropertyImplDecl(Prop->getIdentifier())) + if (IMPDecl->FindPropertyImplDecl( + Prop->getIdentifier(), Prop->getQueryKind())) continue; if (IMPDecl->getInstanceMethod(Prop->getGetterName())) { if (Prop->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_readonly) @@ -1712,7 +1721,7 @@ void Sema::DefaultSynthesizeProperties(Scope *S, ObjCImplDecl* IMPDecl, true, /* property = */ Prop->getIdentifier(), /* ivar = */ Prop->getDefaultSynthIvarName(Context), - Prop->getLocation())); + Prop->getLocation(), Prop->getQueryKind())); if (PIDecl) { Diag(Prop->getLocation(), diag::warn_missing_explicit_synthesis); Diag(IMPDecl->getLocation(), diag::note_while_in_implementation); @@ -1942,8 +1951,8 @@ Sema::AtomicPropertySetterGetterRules (ObjCImplDecl* IMPDecl, if ((Attributes & ObjCPropertyDecl::OBJC_PR_nonatomic) || !(Attributes & ObjCPropertyDecl::OBJC_PR_readwrite)) continue; - if (const ObjCPropertyImplDecl *PIDecl - = IMPDecl->FindPropertyImplDecl(Property->getIdentifier())) { + if (const ObjCPropertyImplDecl *PIDecl = IMPDecl->FindPropertyImplDecl( + Property->getIdentifier(), Property->getQueryKind())) { if (PIDecl->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) continue; if (!LookedUpGetterSetter) { diff --git a/lib/Sema/SemaPseudoObject.cpp b/lib/Sema/SemaPseudoObject.cpp index e5d51f173ca..62c823b363d 100644 --- a/lib/Sema/SemaPseudoObject.cpp +++ b/lib/Sema/SemaPseudoObject.cpp @@ -658,7 +658,8 @@ bool ObjCPropertyOpBuilder::findSetter(bool warn) { SmallString<100> PropertyName = thisPropertyName; PropertyName[0] = front; IdentifierInfo *AltMember = &S.PP.getIdentifierTable().get(PropertyName); - if (ObjCPropertyDecl *prop1 = IFace->FindPropertyDeclaration(AltMember)) + if (ObjCPropertyDecl *prop1 = IFace->FindPropertyDeclaration( + AltMember, prop->getQueryKind())) if (prop != prop1 && (prop1->getSetterMethodDecl() == setter)) { S.Diag(RefExpr->getExprLoc(), diag::error_property_setter_ambiguous_use) << prop << prop1 << setter->getSelector(); diff --git a/test/SemaObjC/objc-class-property.m b/test/SemaObjC/objc-class-property.m index f24e86b6355..bc2bf25193d 100644 --- a/test/SemaObjC/objc-class-property.m +++ b/test/SemaObjC/objc-class-property.m @@ -16,6 +16,7 @@ @interface A : Root { @property(readonly) int ro, ro2; @property (class) int c; @property (class) int c2; +@property (class) int x; @end @implementation A diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index 18851a0dd8b..79c7b628607 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -1077,7 +1077,8 @@ bool CursorVisitor::VisitObjCPropertyDecl(ObjCPropertyDecl *PD) { IdentifierInfo *PropertyId = PD->getIdentifier(); ObjCPropertyDecl *prevDecl = - ObjCPropertyDecl::findPropertyDecl(cast(ID), PropertyId); + ObjCPropertyDecl::findPropertyDecl(cast(ID), PropertyId, + PD->getQueryKind()); if (!prevDecl) return false; From 94a5e3a0eb80e6265d4565668b88be4569319781 Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Thu, 28 Jan 2016 23:36:05 +0000 Subject: [PATCH 070/742] Class Property: change PropertyMap to include isClassProperty. PropertyMap used to map IdentifierInfo (name of the property) to ObjcPropertyDecl *. Now that a class property can have the same name as an instance property, we change PropertyMap to map a pair to ObjcPropertyDecl *. Also update a few places from iterating over instance_properties to iterating over all properties. rdar://23891898 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259119 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/DeclObjC.h | 4 +- lib/AST/DeclObjC.cpp | 14 +++--- lib/Sema/SemaObjCProperty.cpp | 45 ++++++++++++------- .../Checkers/IvarInvalidationChecker.cpp | 2 + 4 files changed, 41 insertions(+), 24 deletions(-) diff --git a/include/clang/AST/DeclObjC.h b/include/clang/AST/DeclObjC.h index def08daf7f3..f89717f698a 100644 --- a/include/clang/AST/DeclObjC.h +++ b/include/clang/AST/DeclObjC.h @@ -1023,7 +1023,9 @@ class ObjCContainerDecl : public NamedDecl, public DeclContext { FindPropertyDeclaration(const IdentifierInfo *PropertyId, ObjCPropertyQueryKind QueryKind) const; - typedef llvm::DenseMap PropertyMap; + typedef llvm::DenseMap, + ObjCPropertyDecl*> PropertyMap; typedef llvm::DenseMap ProtocolPropertyMap; diff --git a/lib/AST/DeclObjC.cpp b/lib/AST/DeclObjC.cpp index 230c2cfd50e..1480a55d56a 100644 --- a/lib/AST/DeclObjC.cpp +++ b/lib/AST/DeclObjC.cpp @@ -369,14 +369,14 @@ ObjCInterfaceDecl::FindPropertyVisibleInPrimaryClass( void ObjCInterfaceDecl::collectPropertiesToImplement(PropertyMap &PM, PropertyDeclOrder &PO) const { - for (auto *Prop : instance_properties()) { - PM[Prop->getIdentifier()] = Prop; + for (auto *Prop : properties()) { + PM[std::make_pair(Prop->getIdentifier(), Prop->isClassProperty())] = Prop; PO.push_back(Prop); } for (const auto *Ext : known_extensions()) { const ObjCCategoryDecl *ClassExt = Ext; - for (auto *Prop : ClassExt->instance_properties()) { - PM[Prop->getIdentifier()] = Prop; + for (auto *Prop : ClassExt->properties()) { + PM[std::make_pair(Prop->getIdentifier(), Prop->isClassProperty())] = Prop; PO.push_back(Prop); } } @@ -1848,9 +1848,11 @@ void ObjCProtocolDecl::collectPropertiesToImplement(PropertyMap &PM, PropertyDeclOrder &PO) const { if (const ObjCProtocolDecl *PDecl = getDefinition()) { - for (auto *Prop : PDecl->instance_properties()) { + for (auto *Prop : PDecl->properties()) { // Insert into PM if not there already. - PM.insert(std::make_pair(Prop->getIdentifier(), Prop)); + PM.insert(std::make_pair( + std::make_pair(Prop->getIdentifier(), Prop->isClassProperty()), + Prop)); PO.push_back(Prop); } // Scan through protocol's protocols. diff --git a/lib/Sema/SemaObjCProperty.cpp b/lib/Sema/SemaObjCProperty.cpp index 35f79b2a3e3..ec057c94377 100644 --- a/lib/Sema/SemaObjCProperty.cpp +++ b/lib/Sema/SemaObjCProperty.cpp @@ -1534,8 +1534,9 @@ static void CollectImmediateProperties(ObjCContainerDecl *CDecl, bool IncludeProtocols = true) { if (ObjCInterfaceDecl *IDecl = dyn_cast(CDecl)) { - for (auto *Prop : IDecl->instance_properties()) - PropMap[Prop->getIdentifier()] = Prop; + for (auto *Prop : IDecl->properties()) + PropMap[std::make_pair(Prop->getIdentifier(), Prop->isClassProperty())] = + Prop; // Collect the properties from visible extensions. for (auto *Ext : IDecl->visible_extensions()) @@ -1548,8 +1549,9 @@ static void CollectImmediateProperties(ObjCContainerDecl *CDecl, } } if (ObjCCategoryDecl *CATDecl = dyn_cast(CDecl)) { - for (auto *Prop : CATDecl->instance_properties()) - PropMap[Prop->getIdentifier()] = Prop; + for (auto *Prop : CATDecl->properties()) + PropMap[std::make_pair(Prop->getIdentifier(), Prop->isClassProperty())] = + Prop; if (IncludeProtocols) { // Scan through class's protocols. for (auto *PI : CATDecl->protocols()) @@ -1557,13 +1559,17 @@ static void CollectImmediateProperties(ObjCContainerDecl *CDecl, } } else if (ObjCProtocolDecl *PDecl = dyn_cast(CDecl)) { - for (auto *Prop : PDecl->instance_properties()) { - ObjCPropertyDecl *PropertyFromSuper = SuperPropMap[Prop->getIdentifier()]; + for (auto *Prop : PDecl->properties()) { + ObjCPropertyDecl *PropertyFromSuper = + SuperPropMap[std::make_pair(Prop->getIdentifier(), + Prop->isClassProperty())]; // Exclude property for protocols which conform to class's super-class, // as super-class has to implement the property. if (!PropertyFromSuper || PropertyFromSuper->getIdentifier() != Prop->getIdentifier()) { - ObjCPropertyDecl *&PropEntry = PropMap[Prop->getIdentifier()]; + ObjCPropertyDecl *&PropEntry = + PropMap[std::make_pair(Prop->getIdentifier(), + Prop->isClassProperty())]; if (!PropEntry) PropEntry = Prop; } @@ -1658,6 +1664,7 @@ void Sema::DefaultSynthesizeProperties(Scope *S, ObjCImplDecl* IMPDecl, ObjCPropertyDecl *Prop = PropertyOrder[i]; // Is there a matching property synthesize/dynamic? if (Prop->isInvalidDecl() || + Prop->isClassProperty() || Prop->getPropertyImplementation() == ObjCPropertyDecl::Optional) continue; // Property may have been synthesized by user. @@ -1678,7 +1685,9 @@ void Sema::DefaultSynthesizeProperties(Scope *S, ObjCImplDecl* IMPDecl, Diag(PID->getLocation(), diag::note_property_synthesize); continue; } - ObjCPropertyDecl *PropInSuperClass = SuperPropMap[Prop->getIdentifier()]; + ObjCPropertyDecl *PropInSuperClass = + SuperPropMap[std::make_pair(Prop->getIdentifier(), + Prop->isClassProperty())]; if (ObjCProtocolDecl *Proto = dyn_cast(Prop->getDeclContext())) { // We won't auto-synthesize properties declared in protocols. @@ -1821,10 +1830,12 @@ void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl, } // Add the properties of 'PDecl' to the list of properties that // need to be implemented. - for (auto *PropDecl : PDecl->instance_properties()) { - if ((*LazyMap)[PropDecl->getIdentifier()]) + for (auto *PropDecl : PDecl->properties()) { + if ((*LazyMap)[std::make_pair(PropDecl->getIdentifier(), + PropDecl->isClassProperty())]) continue; - PropMap[PropDecl->getIdentifier()] = PropDecl; + PropMap[std::make_pair(PropDecl->getIdentifier(), + PropDecl->isClassProperty())] = PropDecl; } } } @@ -1838,7 +1849,7 @@ void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl, SelectorSet InsMap; // Collect property accessors implemented in current implementation. - for (const auto *I : IMPDecl->instance_methods()) + for (const auto *I : IMPDecl->methods()) InsMap.insert(I->getSelector()); ObjCCategoryDecl *C = dyn_cast(CDecl); @@ -1850,7 +1861,7 @@ void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl, // When reporting on missing setter/getters, do not report when // setter/getter is implemented in category's primary class // implementation. - for (const auto *I : IMP->instance_methods()) + for (const auto *I : IMP->methods()) InsMap.insert(I->getSelector()); } @@ -1908,11 +1919,11 @@ Sema::AtomicPropertySetterGetterRules (ObjCImplDecl* IMPDecl, if (getLangOpts().getGC() != LangOptions::NonGC) return; ObjCContainerDecl::PropertyMap PM; - for (auto *Prop : IDecl->instance_properties()) - PM[Prop->getIdentifier()] = Prop; + for (auto *Prop : IDecl->properties()) + PM[std::make_pair(Prop->getIdentifier(), Prop->isClassProperty())] = Prop; for (const auto *Ext : IDecl->known_extensions()) - for (auto *Prop : Ext->instance_properties()) - PM[Prop->getIdentifier()] = Prop; + for (auto *Prop : Ext->properties()) + PM[std::make_pair(Prop->getIdentifier(), Prop->isClassProperty())] = Prop; for (ObjCContainerDecl::PropertyMap::iterator I = PM.begin(), E = PM.end(); I != E; ++I) { diff --git a/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp b/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp index dffff38c91a..153c05bbef1 100644 --- a/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp @@ -390,6 +390,8 @@ visit(const ObjCImplementationDecl *ImplD) const { for (ObjCInterfaceDecl::PropertyMap::iterator I = PropMap.begin(), E = PropMap.end(); I != E; ++I) { const ObjCPropertyDecl *PD = I->second; + if (PD->isClassProperty()) + continue; const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterfaceD, Ivars, &FirstIvarDecl); From 0ac5b6d84cd13116cad3b378257508c1b28406bc Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Fri, 29 Jan 2016 19:05:57 +0000 Subject: [PATCH 071/742] Class Property: parse @dynamic (class). rdar://23891898 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259224 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Parse/ParseObjc.cpp | 26 ++++++++++++++++++++++++++ test/SemaObjC/objc-class-property.m | 5 +++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index 11e9204df0b..4ae2afe068f 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/lib/Parse/ParseObjc.cpp @@ -2355,6 +2355,31 @@ Decl *Parser::ParseObjCPropertyDynamic(SourceLocation atLoc) { assert(Tok.isObjCAtKeyword(tok::objc_dynamic) && "ParseObjCPropertyDynamic(): Expected '@dynamic'"); ConsumeToken(); // consume dynamic + + bool isClassProperty = false; + if (Tok.is(tok::l_paren)) { + ConsumeParen(); + const IdentifierInfo *II = Tok.getIdentifierInfo(); + + if (!II) { + Diag(Tok, diag::err_objc_expected_property_attr) << II; + SkipUntil(tok::r_paren, StopAtSemi); + } else { + SourceLocation AttrName = ConsumeToken(); // consume attribute name + if (II->isStr("class")) { + isClassProperty = true; + if (Tok.isNot(tok::r_paren)) { + Diag(Tok, diag::err_expected) << tok::r_paren; + SkipUntil(tok::r_paren, StopAtSemi); + } else + ConsumeParen(); + } else { + Diag(AttrName, diag::err_objc_expected_property_attr) << II; + SkipUntil(tok::r_paren, StopAtSemi); + } + } + } + while (true) { if (Tok.is(tok::code_completion)) { Actions.CodeCompleteObjCPropertyDefinition(getCurScope()); @@ -2373,6 +2398,7 @@ Decl *Parser::ParseObjCPropertyDynamic(SourceLocation atLoc) { Actions.ActOnPropertyImplDecl( getCurScope(), atLoc, propertyLoc, false, propertyId, nullptr, SourceLocation(), + isClassProperty ? ObjCPropertyQueryKind::OBJC_PR_query_class : ObjCPropertyQueryKind::OBJC_PR_query_unknown); if (Tok.isNot(tok::comma)) diff --git a/test/SemaObjC/objc-class-property.m b/test/SemaObjC/objc-class-property.m index bc2bf25193d..449f106964b 100644 --- a/test/SemaObjC/objc-class-property.m +++ b/test/SemaObjC/objc-class-property.m @@ -20,9 +20,10 @@ @interface A : Root { @end @implementation A -@dynamic x; +@dynamic x; // refers to the instance property +@dynamic (class) x; // refers to the class property @synthesize z; -@dynamic c; +@dynamic c; // refers to the class property @end int test() { From 022b0bccde6c5f61040d89933c3504d7ef7520e4 Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Fri, 29 Jan 2016 19:16:39 +0000 Subject: [PATCH 072/742] Class Property: warn for synthesize on a class property. rdar://23891898 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259226 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 2 ++ lib/Sema/SemaObjCProperty.cpp | 4 ++++ test/SemaObjC/objc-class-property.m | 3 +-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 5d235b1b888..33d2a0e98c3 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -976,6 +976,8 @@ def note_property_synthesize : Note< "property synthesized here">; def error_synthesize_category_decl : Error< "@synthesize not allowed in a category's implementation">; +def error_synthesize_on_class_property : Error< + "@synthesize not allowed on a class property %0">; def error_reference_property : Error< "property of reference type is not supported">; def error_missing_property_interface : Error< diff --git a/lib/Sema/SemaObjCProperty.cpp b/lib/Sema/SemaObjCProperty.cpp index ec057c94377..c9d2da880e1 100644 --- a/lib/Sema/SemaObjCProperty.cpp +++ b/lib/Sema/SemaObjCProperty.cpp @@ -933,6 +933,10 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, Diag(PropertyLoc, diag::error_bad_property_decl) << IDecl->getDeclName(); return nullptr; } + if (property->isClassProperty() && Synthesize) { + Diag(PropertyLoc, diag::error_synthesize_on_class_property) << PropertyId; + return nullptr; + } unsigned PIkind = property->getPropertyAttributesAsWritten(); if ((PIkind & (ObjCPropertyDecl::OBJC_PR_atomic | ObjCPropertyDecl::OBJC_PR_nonatomic) ) == 0) { diff --git a/test/SemaObjC/objc-class-property.m b/test/SemaObjC/objc-class-property.m index 449f106964b..77754400905 100644 --- a/test/SemaObjC/objc-class-property.m +++ b/test/SemaObjC/objc-class-property.m @@ -1,5 +1,4 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -// expected-no-diagnostics @interface Root -(id) alloc; @@ -22,7 +21,7 @@ @interface A : Root { @implementation A @dynamic x; // refers to the instance property @dynamic (class) x; // refers to the class property -@synthesize z; +@synthesize z, c2; // expected-error {{@synthesize not allowed on a class property 'c2'}} @dynamic c; // refers to the class property @end From d41e9a518cd8338411f45ac47427ff9d940167ef Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Fri, 29 Jan 2016 19:22:54 +0000 Subject: [PATCH 073/742] Class Property: generate metadata for class properties in classes. The list of class properties is saved in Old ABI: cls->isa->ext->properties New ABI: cls->isa->ro->properties rdar://23891898 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259229 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CGObjCMac.cpp | 85 ++++++++++++++++++++++++++------------- 1 file changed, 57 insertions(+), 28 deletions(-) diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index f847fa1b7fa..d16eeec1d6e 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/lib/CodeGen/CGObjCMac.cpp @@ -967,7 +967,8 @@ class CGObjCCommonMac : public CodeGen::CGObjCRuntime { llvm::Constant *EmitPropertyList(Twine Name, const Decl *Container, const ObjCContainerDecl *OCD, - const ObjCCommonTypesHelper &ObjCTypes); + const ObjCCommonTypesHelper &ObjCTypes, + bool IsClassProperty); /// EmitProtocolMethodTypes - Generate the array of extended method type /// strings. The return value has type Int8PtrPtrTy. @@ -981,7 +982,8 @@ class CGObjCCommonMac : public CodeGen::CGObjCRuntime { SmallVectorImpl &Properties, const Decl *Container, const ObjCProtocolDecl *Proto, - const ObjCCommonTypesHelper &ObjCTypes); + const ObjCCommonTypesHelper &ObjCTypes, + bool IsClassProperty); /// GetProtocolRef - Return a reference to the internal protocol /// description, creating an empty one if it has not been @@ -1079,7 +1081,8 @@ class CGObjCMac : public CGObjCCommonMac { /// has type ClassExtensionPtrTy. llvm::Constant *EmitClassExtension(const ObjCImplementationDecl *ID, CharUnits instanceSize, - bool hasMRCWeakIvars); + bool hasMRCWeakIvars, + bool isClassProperty); /// EmitClassRef - Return a Value*, of type ObjCTypes.ClassPtrTy, /// for the given class. @@ -2818,7 +2821,7 @@ CGObjCMac::EmitProtocolExtension(const ObjCProtocolDecl *PD, "__OBJC,__cat_cls_meth,regular,no_dead_strip", OptClassMethods), EmitPropertyList("OBJC_$_PROP_PROTO_LIST_" + PD->getName(), nullptr, PD, - ObjCTypes), + ObjCTypes, false), EmitProtocolMethodTypes("OBJC_PROTOCOL_METHOD_TYPES_" + PD->getName(), MethodTypesExt, ObjCTypes)}; @@ -2880,10 +2883,15 @@ PushProtocolProperties(llvm::SmallPtrSet &PropertySet, SmallVectorImpl &Properties, const Decl *Container, const ObjCProtocolDecl *Proto, - const ObjCCommonTypesHelper &ObjCTypes) { + const ObjCCommonTypesHelper &ObjCTypes, + bool IsClassProperty) { for (const auto *P : Proto->protocols()) - PushProtocolProperties(PropertySet, Properties, Container, P, ObjCTypes); - for (const auto *PD : Proto->instance_properties()) { + PushProtocolProperties(PropertySet, Properties, Container, P, ObjCTypes, + IsClassProperty); + + for (const auto *PD : Proto->properties()) { + if (IsClassProperty != PD->isClassProperty()) + continue; if (!PropertySet.insert(PD->getIdentifier()).second) continue; llvm::Constant *Prop[] = { @@ -2909,7 +2917,8 @@ PushProtocolProperties(llvm::SmallPtrSet &PropertySet, llvm::Constant *CGObjCCommonMac::EmitPropertyList(Twine Name, const Decl *Container, const ObjCContainerDecl *OCD, - const ObjCCommonTypesHelper &ObjCTypes) { + const ObjCCommonTypesHelper &ObjCTypes, + bool IsClassProperty) { SmallVector Properties; llvm::SmallPtrSet PropertySet; @@ -2920,11 +2929,16 @@ llvm::Constant *CGObjCCommonMac::EmitPropertyList(Twine Name, }; if (const ObjCInterfaceDecl *OID = dyn_cast(OCD)) for (const ObjCCategoryDecl *ClassExt : OID->known_extensions()) - for (auto *PD : ClassExt->instance_properties()) { + for (auto *PD : ClassExt->properties()) { + if (IsClassProperty != PD->isClassProperty()) + continue; PropertySet.insert(PD->getIdentifier()); AddProperty(PD); } - for (const auto *PD : OCD->instance_properties()) { + + for (const auto *PD : OCD->properties()) { + if (IsClassProperty != PD->isClassProperty()) + continue; // Don't emit duplicate metadata for properties that were already in a // class extension. if (!PropertySet.insert(PD->getIdentifier()).second) @@ -2934,11 +2948,13 @@ llvm::Constant *CGObjCCommonMac::EmitPropertyList(Twine Name, if (const ObjCInterfaceDecl *OID = dyn_cast(OCD)) { for (const auto *P : OID->all_referenced_protocols()) - PushProtocolProperties(PropertySet, Properties, Container, P, ObjCTypes); + PushProtocolProperties(PropertySet, Properties, Container, P, ObjCTypes, + IsClassProperty); } else if (const ObjCCategoryDecl *CD = dyn_cast(OCD)) { for (const auto *P : CD->protocols()) - PushProtocolProperties(PropertySet, Properties, Container, P, ObjCTypes); + PushProtocolProperties(PropertySet, Properties, Container, P, ObjCTypes, + IsClassProperty); } // Return null for empty list. @@ -3079,7 +3095,7 @@ void CGObjCMac::GenerateCategory(const ObjCCategoryImplDecl *OCD) { // If there is no category @interface then there can be no properties. if (Category) { Values[6] = EmitPropertyList("\01l_OBJC_$_PROP_LIST_" + ExtName.str(), - OCD, Category, ObjCTypes); + OCD, Category, ObjCTypes, false); } else { Values[6] = llvm::Constant::getNullValue(ObjCTypes.PropertyListPtrTy); } @@ -3276,7 +3292,8 @@ void CGObjCMac::GenerateClass(const ObjCImplementationDecl *ID) { Values[ 8] = llvm::Constant::getNullValue(ObjCTypes.CachePtrTy); Values[ 9] = Protocols; Values[10] = BuildStrongIvarLayout(ID, CharUnits::Zero(), Size); - Values[11] = EmitClassExtension(ID, Size, hasMRCWeak); + Values[11] = EmitClassExtension(ID, Size, hasMRCWeak, + false/*isClassProperty*/); llvm::Constant *Init = llvm::ConstantStruct::get(ObjCTypes.ClassTy, Values); std::string Name("OBJC_CLASS_"); @@ -3340,8 +3357,9 @@ llvm::Constant *CGObjCMac::EmitMetaClass(const ObjCImplementationDecl *ID, Values[ 9] = Protocols; // ivar_layout for metaclass is always NULL. Values[10] = llvm::Constant::getNullValue(ObjCTypes.Int8PtrTy); - // The class extension is always unused for metaclasses. - Values[11] = llvm::Constant::getNullValue(ObjCTypes.ClassExtensionPtrTy); + // The class extension is used to store class properties for metaclasses. + Values[11] = EmitClassExtension(ID, CharUnits::Zero(), false/*hasMRCWeak*/, + true/*isClassProperty*/); llvm::Constant *Init = llvm::ConstantStruct::get(ObjCTypes.ClassTy, Values); @@ -3415,19 +3433,26 @@ llvm::Value *CGObjCMac::EmitSuperClassRef(const ObjCInterfaceDecl *ID) { */ llvm::Constant * CGObjCMac::EmitClassExtension(const ObjCImplementationDecl *ID, - CharUnits InstanceSize, bool hasMRCWeakIvars) { + CharUnits InstanceSize, bool hasMRCWeakIvars, + bool isClassProperty) { uint64_t Size = CGM.getDataLayout().getTypeAllocSize(ObjCTypes.ClassExtensionTy); llvm::Constant *Values[3]; Values[0] = llvm::ConstantInt::get(ObjCTypes.IntTy, Size); - Values[1] = BuildWeakIvarLayout(ID, CharUnits::Zero(), InstanceSize, - hasMRCWeakIvars); - Values[2] = EmitPropertyList("\01l_OBJC_$_PROP_LIST_" + ID->getName(), - ID, ID->getClassInterface(), ObjCTypes); + Values[1] = nullptr; + if (!isClassProperty) + Values[1] = BuildWeakIvarLayout(ID, CharUnits::Zero(), InstanceSize, + hasMRCWeakIvars); + if (isClassProperty) + Values[2] = EmitPropertyList("\01l_OBJC_$_CLASS_PROP_LIST_" + ID->getName(), + ID, ID->getClassInterface(), ObjCTypes, true); + else + Values[2] = EmitPropertyList("\01l_OBJC_$_PROP_LIST_" + ID->getName(), + ID, ID->getClassInterface(), ObjCTypes, false); // Return null if no extension bits are used. - if (Values[1]->isNullValue() && Values[2]->isNullValue()) + if ((!Values[1] || Values[1]->isNullValue()) && Values[2]->isNullValue()) return llvm::Constant::getNullValue(ObjCTypes.ClassExtensionPtrTy); llvm::Constant *Init = @@ -5817,13 +5842,16 @@ llvm::GlobalVariable * CGObjCNonFragileABIMac::BuildClassRoTInitializer( if (flags & NonFragileABI_Class_Meta) { Values[ 7] = llvm::Constant::getNullValue(ObjCTypes.IvarListnfABIPtrTy); Values[ 8] = GetIvarLayoutName(nullptr, ObjCTypes); - Values[ 9] = llvm::Constant::getNullValue(ObjCTypes.PropertyListPtrTy); + Values[ 9] = EmitPropertyList( + "\01l_OBJC_$_CLASS_PROP_LIST_" + ID->getObjCRuntimeNameAsString(), + ID, ID->getClassInterface(), ObjCTypes, true); } else { Values[ 7] = EmitIvarList(ID); Values[ 8] = BuildWeakIvarLayout(ID, beginInstance, endInstance, hasMRCWeak); - Values[ 9] = EmitPropertyList("\01l_OBJC_$_PROP_LIST_" + ID->getObjCRuntimeNameAsString(), - ID, ID->getClassInterface(), ObjCTypes); + Values[ 9] = EmitPropertyList( + "\01l_OBJC_$_PROP_LIST_" + ID->getObjCRuntimeNameAsString(), + ID, ID->getClassInterface(), ObjCTypes, false); } llvm::Constant *Init = llvm::ConstantStruct::get(ObjCTypes.ClassRonfABITy, Values); @@ -6170,7 +6198,7 @@ void CGObjCNonFragileABIMac::GenerateCategory(const ObjCCategoryImplDecl *OCD) { Category->protocol_begin(), Category->protocol_end()); Values[5] = EmitPropertyList("\01l_OBJC_$_PROP_LIST_" + ExtName.str(), - OCD, Category, ObjCTypes); + OCD, Category, ObjCTypes, false); } else { Values[4] = llvm::Constant::getNullValue(ObjCTypes.ProtocolListnfABIPtrTy); Values[5] = llvm::Constant::getNullValue(ObjCTypes.PropertyListPtrTy); @@ -6480,8 +6508,9 @@ llvm::Constant *CGObjCNonFragileABIMac::GetOrEmitProtocol( + PD->getObjCRuntimeNameAsString(), "__DATA, __objc_const", OptClassMethods); - Values[7] = EmitPropertyList("\01l_OBJC_$_PROP_LIST_" + PD->getObjCRuntimeNameAsString(), - nullptr, PD, ObjCTypes); + Values[7] = EmitPropertyList( + "\01l_OBJC_$_PROP_LIST_" + PD->getObjCRuntimeNameAsString(), + nullptr, PD, ObjCTypes, false); uint32_t Size = CGM.getDataLayout().getTypeAllocSize(ObjCTypes.ProtocolnfABITy); Values[8] = llvm::ConstantInt::get(ObjCTypes.IntTy, Size); From 89498a57e7f8a909688e5ffa0b90bd167233578d Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Fri, 29 Jan 2016 23:45:01 +0000 Subject: [PATCH 074/742] Class Property: generate metadata for class properties in categories. The list of class properties is saved in Old ABI: category->class_properties (category->size will be updated as well) New ABI: category->class_properties (a flag in objc_image_info to indicate whether or not the list of class properties is present) rdar://23891898 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259267 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CGObjCMac.cpp | 25 ++++++++++++++++---- test/CodeGenObjC/metadata-class-properties.m | 24 +++++++++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) create mode 100644 test/CodeGenObjC/metadata-class-properties.m diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index d16eeec1d6e..0fd5feb6e99 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/lib/CodeGen/CGObjCMac.cpp @@ -3047,6 +3047,7 @@ CGObjCMac::EmitMethodDescList(Twine Name, const char *Section, struct _objc_protocol_list *protocols; uint32_t size; // struct _objc_property_list *instance_properties; + struct _objc_property_list *class_properties; }; */ void CGObjCMac::GenerateCategory(const ObjCCategoryImplDecl *OCD) { @@ -3073,7 +3074,7 @@ void CGObjCMac::GenerateCategory(const ObjCCategoryImplDecl *OCD) { // Class methods should always be defined. ClassMethods.push_back(GetMethodConstant(I)); - llvm::Constant *Values[7]; + llvm::Constant *Values[8]; Values[0] = GetClassName(OCD->getName()); Values[1] = GetClassName(Interface->getObjCRuntimeNameAsString()); LazySymbols.insert(Interface->getIdentifier()); @@ -3096,8 +3097,11 @@ void CGObjCMac::GenerateCategory(const ObjCCategoryImplDecl *OCD) { if (Category) { Values[6] = EmitPropertyList("\01l_OBJC_$_PROP_LIST_" + ExtName.str(), OCD, Category, ObjCTypes, false); + Values[7] = EmitPropertyList("\01l_OBJC_$_CLASS_PROP_LIST_" + ExtName.str(), + OCD, Category, ObjCTypes, true); } else { Values[6] = llvm::Constant::getNullValue(ObjCTypes.PropertyListPtrTy); + Values[7] = llvm::Constant::getNullValue(ObjCTypes.PropertyListPtrTy); } llvm::Constant *Init = llvm::ConstantStruct::get(ObjCTypes.CategoryTy, @@ -4482,7 +4486,8 @@ enum ImageInfoFlags { // A flag indicating that the module has no instances of a @synthesize of a // superclass variable. eImageInfo_CorrectedSynthesize = (1 << 4), // This flag is no longer set by clang. - eImageInfo_ImageIsSimulated = (1 << 5) + eImageInfo_ImageIsSimulated = (1 << 5), + eImageInfo_ClassProperties = (1 << 6) }; void CGObjCCommonMac::EmitImageInfo() { @@ -4534,6 +4539,10 @@ void CGObjCCommonMac::EmitImageInfo() { Triple.getArch() == llvm::Triple::x86_64)) Mod.addModuleFlag(llvm::Module::Error, "Objective-C Is Simulated", eImageInfo_ImageIsSimulated); + + // Indicate whether we are generating class properties. + Mod.addModuleFlag(llvm::Module::Error, "Objective-C Class Properties", + eImageInfo_ClassProperties); } // struct objc_module { @@ -5389,12 +5398,14 @@ ObjCTypesHelper::ObjCTypesHelper(CodeGen::CodeGenModule &cgm) // struct _objc_protocol_list *protocols; // uint32_t size; // sizeof(struct _objc_category) // struct _objc_property_list *instance_properties;// category's @property + // struct _objc_property_list *class_properties; // } CategoryTy = llvm::StructType::create("struct._objc_category", Int8PtrTy, Int8PtrTy, MethodListPtrTy, MethodListPtrTy, ProtocolListPtrTy, - IntTy, PropertyListPtrTy, nullptr); + IntTy, PropertyListPtrTy, PropertyListPtrTy, + nullptr); // Global metadata structures @@ -5567,6 +5578,7 @@ ObjCNonFragileABITypesHelper::ObjCNonFragileABITypesHelper(CodeGen::CodeGenModul // const struct _method_list_t * const class_methods; // const struct _protocol_list_t * const protocols; // const struct _prop_list_t * const properties; + // const struct _prop_list_t * const class_properties; // } CategorynfABITy = llvm::StructType::create("struct._category_t", Int8PtrTy, ClassnfABIPtrTy, @@ -5574,6 +5586,7 @@ ObjCNonFragileABITypesHelper::ObjCNonFragileABITypesHelper(CodeGen::CodeGenModul MethodListnfABIPtrTy, ProtocolListnfABIPtrTy, PropertyListPtrTy, + PropertyListPtrTy, nullptr); // New types for nonfragile abi messaging. @@ -6134,6 +6147,7 @@ llvm::Value *CGObjCNonFragileABIMac::GenerateProtocolRef(CodeGenFunction &CGF, /// const struct _method_list_t * const class_methods; /// const struct _protocol_list_t * const protocols; /// const struct _prop_list_t * const properties; +/// const struct _prop_list_t * const class_properties; /// } /// void CGObjCNonFragileABIMac::GenerateCategory(const ObjCCategoryImplDecl *OCD) { @@ -6148,7 +6162,7 @@ void CGObjCNonFragileABIMac::GenerateCategory(const ObjCCategoryImplDecl *OCD) { llvm::SmallString<64> ExtClassName(getClassSymbolPrefix()); ExtClassName += Interface->getObjCRuntimeNameAsString(); - llvm::Constant *Values[6]; + llvm::Constant *Values[7]; Values[0] = GetClassName(OCD->getIdentifier()->getName()); // meta-class entry symbol llvm::Constant *ClassGV = GetClassGlobal(ExtClassName.str(), @@ -6199,9 +6213,12 @@ void CGObjCNonFragileABIMac::GenerateCategory(const ObjCCategoryImplDecl *OCD) { Category->protocol_end()); Values[5] = EmitPropertyList("\01l_OBJC_$_PROP_LIST_" + ExtName.str(), OCD, Category, ObjCTypes, false); + Values[6] = EmitPropertyList("\01l_OBJC_$_CLASS_PROP_LIST_" + ExtName.str(), + OCD, Category, ObjCTypes, true); } else { Values[4] = llvm::Constant::getNullValue(ObjCTypes.ProtocolListnfABIPtrTy); Values[5] = llvm::Constant::getNullValue(ObjCTypes.PropertyListPtrTy); + Values[6] = llvm::Constant::getNullValue(ObjCTypes.PropertyListPtrTy); } llvm::Constant *Init = diff --git a/test/CodeGenObjC/metadata-class-properties.m b/test/CodeGenObjC/metadata-class-properties.m new file mode 100644 index 00000000000..f5258424cac --- /dev/null +++ b/test/CodeGenObjC/metadata-class-properties.m @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -o - -fobjc-runtime=macosx-fragile-10.5 %s | FileCheck -check-prefix=CHECK-FRAGILE %s + +// CHECK: @"\01l_OBJC_$_CLASS_PROP_LIST_Foo_$_Category" = private global {{.*}} section "__DATA, __objc_const", align 8 +// CHECK: @"\01l_OBJC_$_CATEGORY_Foo_$_Category" = private global %struct._category_t { {{.*}} @"\01l_OBJC_$_CLASS_PROP_LIST_Foo_$_Category" {{.*}} }, section "__DATA, __objc_const", align 8 + +// CHECK: !{i32 1, !"Objective-C Class Properties", i32 64} + +// CHECK-FRAGILE: @"\01l_OBJC_$_CLASS_PROP_LIST_Foo_Category" = private global {{.*}} section "__OBJC,__property,regular,no_dead_strip", align 8 +// CHECK-FRAGILE: @OBJC_CATEGORY_Foo_Category = private global %struct._objc_category { {{.*}}, i32 64, {{.*}} @"\01l_OBJC_$_CLASS_PROP_LIST_Foo_Category" {{.*}} }, section "__OBJC,__category,regular,no_dead_strip", align 8 + +// CHECK-FRAGILE: !{i32 1, !"Objective-C Class Properties", i32 64} + +@interface Foo @end + +@protocol Proto +@property (class, readonly) int proto_property; +@end + +@interface Foo (Category) @end + +@implementation Foo (Category) ++(int)proto_property { return 0; } +@end From 6ae30dcb2b58fff7543a04239a5ada3ed269c4ca Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Fri, 29 Jan 2016 23:46:55 +0000 Subject: [PATCH 075/742] Class Property: generate metadata for class properties in protocols. The list of class properties is saved in Old ABI: protocol->ext->class_properties (protocol->ext->size will be updated) New ABI: protocol->class_properties (protocol->size will be updated) rdar://23891898 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259268 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CGObjCMac.cpp | 21 +++++++++++++++----- test/CodeGenObjC/metadata-class-properties.m | 4 ++++ test/CodeGenObjC/objc2-protocol-metadata.m | 2 +- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index 0fd5feb6e99..fdbc983aa5b 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/lib/CodeGen/CGObjCMac.cpp @@ -2803,6 +2803,7 @@ llvm::Constant *CGObjCMac::GetOrEmitProtocolRef(const ObjCProtocolDecl *PD) { struct objc_method_description_list *optional_class_methods; struct objc_property_list *instance_properties; const char ** extendedMethodTypes; + struct objc_property_list *class_properties; }; */ llvm::Constant * @@ -2823,11 +2824,14 @@ CGObjCMac::EmitProtocolExtension(const ObjCProtocolDecl *PD, EmitPropertyList("OBJC_$_PROP_PROTO_LIST_" + PD->getName(), nullptr, PD, ObjCTypes, false), EmitProtocolMethodTypes("OBJC_PROTOCOL_METHOD_TYPES_" + PD->getName(), - MethodTypesExt, ObjCTypes)}; + MethodTypesExt, ObjCTypes), + EmitPropertyList("OBJC_$_CLASS_PROP_PROTO_LIST_" + PD->getName(), nullptr, + PD, ObjCTypes, true)}; // Return null if no extension bits are used. if (Values[1]->isNullValue() && Values[2]->isNullValue() && - Values[3]->isNullValue() && Values[4]->isNullValue()) + Values[3]->isNullValue() && Values[4]->isNullValue() && + Values[5]->isNullValue()) return llvm::Constant::getNullValue(ObjCTypes.ProtocolExtensionPtrTy); llvm::Constant *Init = @@ -5292,12 +5296,13 @@ ObjCTypesHelper::ObjCTypesHelper(CodeGen::CodeGenModule &cgm) // struct _objc_method_description_list *optional_class_methods; // struct _objc_property_list *instance_properties; // const char ** extendedMethodTypes; + // struct _objc_property_list *class_properties; // } ProtocolExtensionTy = llvm::StructType::create("struct._objc_protocol_extension", IntTy, MethodDescriptionListPtrTy, MethodDescriptionListPtrTy, PropertyListPtrTy, - Int8PtrPtrTy, nullptr); + Int8PtrPtrTy, PropertyListPtrTy, nullptr); // struct _objc_protocol_extension * ProtocolExtensionPtrTy = llvm::PointerType::getUnqual(ProtocolExtensionTy); @@ -5473,6 +5478,7 @@ ObjCNonFragileABITypesHelper::ObjCNonFragileABITypesHelper(CodeGen::CodeGenModul // const uint32_t flags; // = 0 // const char ** extendedMethodTypes; // const char *demangledName; + // const struct _prop_list_t * class_properties; // } // Holder for struct _protocol_list_t * @@ -5485,7 +5491,7 @@ ObjCNonFragileABITypesHelper::ObjCNonFragileABITypesHelper(CodeGen::CodeGenModul MethodListnfABIPtrTy, MethodListnfABIPtrTy, MethodListnfABIPtrTy, MethodListnfABIPtrTy, PropertyListPtrTy, IntTy, IntTy, Int8PtrPtrTy, - Int8PtrTy, + Int8PtrTy, PropertyListPtrTy, nullptr); // struct _protocol_t* @@ -6450,6 +6456,7 @@ llvm::Constant *CGObjCNonFragileABIMac::GetOrEmitProtocolRef( /// const uint32_t flags; // = 0 /// const char ** extendedMethodTypes; /// const char *demangledName; +/// const struct _prop_list_t * class_properties; /// } /// @endcode /// @@ -6501,7 +6508,7 @@ llvm::Constant *CGObjCNonFragileABIMac::GetOrEmitProtocol( MethodTypesExt.insert(MethodTypesExt.end(), OptMethodTypesExt.begin(), OptMethodTypesExt.end()); - llvm::Constant *Values[12]; + llvm::Constant *Values[13]; // isa is NULL Values[0] = llvm::Constant::getNullValue(ObjCTypes.ObjectPtrTy); Values[1] = GetClassName(PD->getObjCRuntimeNameAsString()); @@ -6537,6 +6544,10 @@ llvm::Constant *CGObjCNonFragileABIMac::GetOrEmitProtocol( MethodTypesExt, ObjCTypes); // const char *demangledName; Values[11] = llvm::Constant::getNullValue(ObjCTypes.Int8PtrTy); + + Values[12] = EmitPropertyList( + "\01l_OBJC_$_CLASS_PROP_LIST_" + PD->getObjCRuntimeNameAsString(), + nullptr, PD, ObjCTypes, true); llvm::Constant *Init = llvm::ConstantStruct::get(ObjCTypes.ProtocolnfABITy, Values); diff --git a/test/CodeGenObjC/metadata-class-properties.m b/test/CodeGenObjC/metadata-class-properties.m index f5258424cac..93557662355 100644 --- a/test/CodeGenObjC/metadata-class-properties.m +++ b/test/CodeGenObjC/metadata-class-properties.m @@ -1,11 +1,15 @@ // RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -o - %s | FileCheck %s // RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -o - -fobjc-runtime=macosx-fragile-10.5 %s | FileCheck -check-prefix=CHECK-FRAGILE %s +// CHECK: @"\01l_OBJC_$_CLASS_PROP_LIST_Proto" = private global {{.*}} section "__DATA, __objc_const", align 8 +// CHECK: @"\01l_OBJC_PROTOCOL_$_Proto" = {{.*}} global %struct._protocol_t { {{.*}} i32 96, i32 {{.*}} @"\01l_OBJC_$_CLASS_PROP_LIST_Proto" {{.*}} } // CHECK: @"\01l_OBJC_$_CLASS_PROP_LIST_Foo_$_Category" = private global {{.*}} section "__DATA, __objc_const", align 8 // CHECK: @"\01l_OBJC_$_CATEGORY_Foo_$_Category" = private global %struct._category_t { {{.*}} @"\01l_OBJC_$_CLASS_PROP_LIST_Foo_$_Category" {{.*}} }, section "__DATA, __objc_const", align 8 // CHECK: !{i32 1, !"Objective-C Class Properties", i32 64} +// CHECK-FRAGILE: @"OBJC_$_CLASS_PROP_PROTO_LIST_Proto" = private global {{.*}} section "__OBJC,__property,regular,no_dead_strip", align 8 +// CHECK-FRAGILE: @"\01l_OBJC_PROTOCOLEXT_Proto" = private global %struct._objc_protocol_extension { i32 48, {{.*}} @"OBJC_$_CLASS_PROP_PROTO_LIST_Proto" {{.*}} }, align 8 // CHECK-FRAGILE: @"\01l_OBJC_$_CLASS_PROP_LIST_Foo_Category" = private global {{.*}} section "__OBJC,__property,regular,no_dead_strip", align 8 // CHECK-FRAGILE: @OBJC_CATEGORY_Foo_Category = private global %struct._objc_category { {{.*}}, i32 64, {{.*}} @"\01l_OBJC_$_CLASS_PROP_LIST_Foo_Category" {{.*}} }, section "__OBJC,__category,regular,no_dead_strip", align 8 diff --git a/test/CodeGenObjC/objc2-protocol-metadata.m b/test/CodeGenObjC/objc2-protocol-metadata.m index 191016be851..8e186fcee43 100644 --- a/test/CodeGenObjC/objc2-protocol-metadata.m +++ b/test/CodeGenObjC/objc2-protocol-metadata.m @@ -14,4 +14,4 @@ @implementation INTF + ClsP { return 0; } @end -// CHECK: %struct._protocol_t = type { i8*, i8*, %struct._objc_protocol_list*, %struct.__method_list_t*, %struct.__method_list_t*, %struct.__method_list_t*, %struct.__method_list_t*, %struct._prop_list_t*, i32, i32, i8**, i8* } +// CHECK: %struct._protocol_t = type { i8*, i8*, %struct._objc_protocol_list*, %struct.__method_list_t*, %struct.__method_list_t*, %struct.__method_list_t*, %struct.__method_list_t*, %struct._prop_list_t*, i32, i32, i8**, i8*, %struct._prop_list_t* } From f55b053248ee74770f20fcbb8472ef6a3e4ef08e Mon Sep 17 00:00:00 2001 From: Chris Bieneman Date: Fri, 5 Feb 2016 01:22:03 +0000 Subject: [PATCH 076/742] [CMake] Improve the clang order-file generation workflow Summary: With this change generating clang order files using dtrace uses the following workflow: cmake ninja generate-order-file ninja clang This patch works by setting a default path to the order file (which can be overridden by the user). If the order file doesn't exist during configuration CMake will create an empty one. CMake then ties up the dependencies between the clang link job and the order file, and generate-order-file overwrites CLANG_ORDER_FILE with the new order file. Reviewers: bogner Subscribers: cfe-commits Differential Revision: http://reviews.llvm.org/D16896 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259862 91177308-0d34-0410-b5e6-96231b3b80d8 --- CMakeLists.txt | 12 +++++++++++- tools/driver/CMakeLists.txt | 6 +++++- utils/perf-training/CMakeLists.txt | 3 +-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ca3b51fddd4..bb6194385b5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -586,9 +586,19 @@ if( CLANG_INCLUDE_DOCS ) add_subdirectory(docs) endif() -set(CLANG_ORDER_FILE "" CACHE FILEPATH +# this line is needed as a cleanup to ensure that any CMakeCaches with the old +# default value get updated to the new default. +if(CLANG_ORDER_FILE STREQUAL "") + unset(CLANG_ORDER_FILE CACHE) +endif() + +set(CLANG_ORDER_FILE ${CMAKE_CURRENT_BINARY_DIR}/clang.order CACHE FILEPATH "Order file to use when compiling clang in order to improve startup time.") +if(NOT EXISTS ${CLANG_ORDER_FILE}) + execute_process(COMMAND ${CMAKE_COMMAND} -E touch ${CLANG_ORDER_FILE}) +endif() + if (CLANG_BUILT_STANDALONE OR CMAKE_VERSION VERSION_EQUAL 3 OR CMAKE_VERSION VERSION_GREATER 3) # Generate a list of CMake library targets so that other CMake projects can diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt index 73ad4fdce21..536d778d583 100644 --- a/tools/driver/CMakeLists.txt +++ b/tools/driver/CMakeLists.txt @@ -89,8 +89,12 @@ if (APPLE) set(TOOL_INFO_BUILD_VERSION) endif() -if(CLANG_ORDER_FILE) +check_cxx_compiler_flag("-Wl,-order_file,${CLANG_ORDER_FILE}" + LINKER_HAS_ORDER_FILE_FLAG) + +if(LINKER_HAS_ORDER_FILE_FLAG) target_link_libraries(clang "-Wl,-order_file,${CLANG_ORDER_FILE}") + set_target_properties(clang PROPERTIES LINK_DEPENDS ${CLANG_ORDER_FILE}) endif() if(WITH_POLLY AND LINK_POLLY_INTO_TOOLS) diff --git a/utils/perf-training/CMakeLists.txt b/utils/perf-training/CMakeLists.txt index 32f7c84555b..f8647a0e44f 100644 --- a/utils/perf-training/CMakeLists.txt +++ b/utils/perf-training/CMakeLists.txt @@ -55,9 +55,8 @@ if(DTRACE) COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/perf-helper.py clean ${CMAKE_CURRENT_BINARY_DIR} dtrace COMMENT "Clearing old dtrace data") - add_custom_target(generate-order-file - COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/perf-helper.py gen-order-file --binary $ --output ${CMAKE_CURRENT_BINARY_DIR}/clang.order ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/perf-helper.py gen-order-file --binary $ --output ${CLANG_ORDER_FILE} ${CMAKE_CURRENT_BINARY_DIR} COMMENT "Generating order file" DEPENDS generate-dtrace-logs) endif() From c44f4930ca096133d65c705e47eb87c927f7b7a8 Mon Sep 17 00:00:00 2001 From: Chris Bieneman Date: Fri, 5 Feb 2016 01:27:31 +0000 Subject: [PATCH 077/742] [CMake] Trying to fix a bot failure I introduced in r259862 CMake caching behavior makes me sad. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259864 91177308-0d34-0410-b5e6-96231b3b80d8 --- tools/driver/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt index 536d778d583..36457340569 100644 --- a/tools/driver/CMakeLists.txt +++ b/tools/driver/CMakeLists.txt @@ -92,7 +92,7 @@ endif() check_cxx_compiler_flag("-Wl,-order_file,${CLANG_ORDER_FILE}" LINKER_HAS_ORDER_FILE_FLAG) -if(LINKER_HAS_ORDER_FILE_FLAG) +if(LINKER_HAS_ORDER_FILE_FLAG AND CLANG_ORDER_FILE) target_link_libraries(clang "-Wl,-order_file,${CLANG_ORDER_FILE}") set_target_properties(clang PROPERTIES LINK_DEPENDS ${CLANG_ORDER_FILE}) endif() From 858768c71f89dc662e86f23d54789e2c632bb96d Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Wed, 3 Feb 2016 03:26:19 +0000 Subject: [PATCH 078/742] Make CF constant string decl visible to name lookup to fix module errors The return type of the __builtin___*StringMakeConstantString functions is a pointer to a struct, so we need that struct to be visible to name lookup so that we will correctly merge multiple declarations of that type if they come from different modules. Incidentally, to make this visible to name lookup we need to rename the type to __NSConstantString, since the real NSConstantString is an Objective-C interface type. This shouldn't affect anyone outside the compiler since users of the constant string builtins cast the result immediately to CFStringRef. Since this struct type is otherwise implicitly created by the AST context and cannot access namelookup, we make this a predefined type and initialize it in Sema. Note: this issue of builtins that refer to types not visible to name lookup technically also affects other builtins (e.g. objc_msgSendSuper), but in all other cases the builtin is a library builtin and the issue goes away if you include the library that defines the types it uses, unlike for these constant string builtins. rdar://problem/24425801 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259624 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/ASTContext.h | 1 + include/clang/Serialization/ASTBitCodes.h | 5 +++- lib/AST/ASTContext.cpp | 14 +++++++--- lib/Sema/Sema.cpp | 4 +++ lib/Serialization/ASTReader.cpp | 3 +++ lib/Serialization/ASTWriter.cpp | 2 ++ test/CodeGenObjC/2010-02-01-utf16-with-null.m | 4 +-- test/CodeGenObjC/arc-no-arc-exceptions.m | 2 +- test/CodeGenObjC/tentative-cfconstantstring.m | 4 +-- test/Modules/Inputs/builtin.h | 7 +++++ test/Modules/builtins.m | 26 +++++++++++++------ 11 files changed, 54 insertions(+), 18 deletions(-) diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index abf92948bb4..1d053411ebc 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -1385,6 +1385,7 @@ class ASTContext : public RefCountedBase { return QualType(); } void setCFConstantStringType(QualType T); + TagDecl *getCFConstantStringDecl() const; // This setter/getter represents the ObjC type for an NSConstantString. void setObjCConstantStringInterface(ObjCInterfaceDecl *Decl); diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index 0dfb8cf3714..4bb1f5eacfe 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -987,13 +987,16 @@ namespace clang { /// \brief The internal '__make_integer_seq' template. PREDEF_DECL_MAKE_INTEGER_SEQ_ID = 13, + + /// \brief The internal '__NSConstantString' type. + PREDEF_DECL_CF_CONSTANT_STRING_ID = 14, }; /// \brief The number of declaration IDs that are predefined. /// /// For more information about predefined declarations, see the /// \c PredefinedDeclIDs type and the PREDEF_DECL_*_ID constants. - const unsigned int NUM_PREDEF_DECL_IDS = 14; + const unsigned int NUM_PREDEF_DECL_IDS = 15; /// \brief Record code for a list of local redeclarations of a declaration. const unsigned int LOCAL_REDECLARATIONS = 50; diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 02b3e51d1de..fc5ff8668e9 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -4868,10 +4868,11 @@ int ASTContext::getIntegerTypeOrder(QualType LHS, QualType RHS) const { return 1; } -// getCFConstantStringType - Return the type used for constant CFStrings. -QualType ASTContext::getCFConstantStringType() const { +TagDecl *ASTContext::getCFConstantStringDecl() const { if (!CFConstantStringTypeDecl) { - CFConstantStringTypeDecl = buildImplicitRecord("NSConstantString"); + // This type is designed to be compatible with NSConstantString, but cannot + // use the same name, since NSConstantString is an interface. + CFConstantStringTypeDecl = buildImplicitRecord("__NSConstantString"); CFConstantStringTypeDecl->startDefinition(); QualType FieldTypes[4]; @@ -4901,7 +4902,12 @@ QualType ASTContext::getCFConstantStringType() const { CFConstantStringTypeDecl->completeDefinition(); } - return getTagDeclType(CFConstantStringTypeDecl); + return CFConstantStringTypeDecl; +} + +// getCFConstantStringType - Return the type used for constant CFStrings. +QualType ASTContext::getCFConstantStringType() const { + return getTagDeclType(getCFConstantStringDecl()); } QualType ASTContext::getObjCSuperType() const { diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index 707c5eb9257..0119ddac1fa 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -189,6 +189,10 @@ void Sema::Initialize() { DeclarationName Protocol = &Context.Idents.get("Protocol"); if (IdResolver.begin(Protocol) == IdResolver.end()) PushOnScopeChains(Context.getObjCProtocolDecl(), TUScope); + + DeclarationName ConstantString = &Context.Idents.get("NSConstantString"); + if (IdResolver.begin(ConstantString) == IdResolver.end()) + PushOnScopeChains(Context.getCFConstantStringDecl(), TUScope); } // Initialize Microsoft "predefined C++ types". diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 97fbb45e121..b7c869c23ba 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -6444,6 +6444,9 @@ static Decl *getPredefinedDecl(ASTContext &Context, PredefinedDeclIDs ID) { case PREDEF_DECL_MAKE_INTEGER_SEQ_ID: return Context.getMakeIntegerSeqDecl(); + + case PREDEF_DECL_CF_CONSTANT_STRING_ID: + return Context.getCFConstantStringDecl(); } llvm_unreachable("PredefinedDeclIDs unknown enum value"); } diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index ec04cd6c1fa..a590e188eca 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -4152,6 +4152,8 @@ uint64_t ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot, RegisterPredefDecl(Context.ExternCContext, PREDEF_DECL_EXTERN_C_CONTEXT_ID); RegisterPredefDecl(Context.MakeIntegerSeqDecl, PREDEF_DECL_MAKE_INTEGER_SEQ_ID); + RegisterPredefDecl(Context.CFConstantStringTypeDecl, + PREDEF_DECL_CF_CONSTANT_STRING_ID); // Build a record containing all of the tentative definitions in this file, in // TentativeDefinitions order. Generally, this record will be empty for diff --git a/test/CodeGenObjC/2010-02-01-utf16-with-null.m b/test/CodeGenObjC/2010-02-01-utf16-with-null.m index 46ce3b289fc..856ac9ac97b 100644 --- a/test/CodeGenObjC/2010-02-01-utf16-with-null.m +++ b/test/CodeGenObjC/2010-02-01-utf16-with-null.m @@ -2,6 +2,6 @@ // rdar://7589850 // CHECK: @.str = private unnamed_addr constant [9 x i16] [i16 103, i16 111, i16 111, i16 100, i16 0, i16 98, i16 121, i16 101, i16 0], section "__TEXT,__ustring", align 2 -// CHECK: @_unnamed_cfstring_ = private constant %struct.NSConstantString { i32* getelementptr inbounds ([0 x i32], [0 x i32]* @__CFConstantStringClassReference, i32 0, i32 0), i32 2000, i8* bitcast ([9 x i16]* @.str to i8*), i32 8 }, section "__DATA,__cfstring" -// CHECK: @P = global i8* bitcast (%struct.NSConstantString* @_unnamed_cfstring_ to i8*), align 4 +// CHECK: @_unnamed_cfstring_ = private constant %struct.__NSConstantString { i32* getelementptr inbounds ([0 x i32], [0 x i32]* @__CFConstantStringClassReference, i32 0, i32 0), i32 2000, i8* bitcast ([9 x i16]* @.str to i8*), i32 8 }, section "__DATA,__cfstring" +// CHECK: @P = global i8* bitcast (%struct.__NSConstantString* @_unnamed_cfstring_ to i8*), align 4 void *P = @"good\0bye"; diff --git a/test/CodeGenObjC/arc-no-arc-exceptions.m b/test/CodeGenObjC/arc-no-arc-exceptions.m index 82977b0a175..30424e7fb36 100644 --- a/test/CodeGenObjC/arc-no-arc-exceptions.m +++ b/test/CodeGenObjC/arc-no-arc-exceptions.m @@ -34,7 +34,7 @@ void test1(id x) { void NSLog(id, ...); // CHECK-LABEL: define void @test2( -// CHECK: invoke void (i8*, ...) @NSLog(i8* bitcast (%struct.NSConstantString* @_unnamed_cfstring_ to i8*), i32* %{{.*}}) +// CHECK: invoke void (i8*, ...) @NSLog(i8* bitcast (%struct.__NSConstantString* @_unnamed_cfstring_ to i8*), i32* %{{.*}}) // CHECK: to label %{{.*}} unwind label %{{.*}}, !clang.arc.no_objc_arc_exceptions ! // NO-METADATA-LABEL: define void @test2( // NO-METADATA-NOT: !clang.arc.no_objc_arc_exceptions diff --git a/test/CodeGenObjC/tentative-cfconstantstring.m b/test/CodeGenObjC/tentative-cfconstantstring.m index 5b3c3bd9249..0c692c37c95 100644 --- a/test/CodeGenObjC/tentative-cfconstantstring.m +++ b/test/CodeGenObjC/tentative-cfconstantstring.m @@ -32,12 +32,12 @@ -(void)someMethod { @end // CHECK: @__CFConstantStringClassReference = common global [24 x i32] zeroinitializer, align 16 -// CHECK: @_unnamed_cfstring_{{.*}} = private constant %struct.NSConstantString { i32* getelementptr inbounds ([24 x i32], [24 x i32]* @__CFConstantStringClassReference, i32 0, i32 0) +// CHECK: @_unnamed_cfstring_{{.*}} = private constant %struct.__NSConstantString { i32* getelementptr inbounds ([24 x i32], [24 x i32]* @__CFConstantStringClassReference, i32 0, i32 0) // CHECK-LABEL: define internal void @_inlineFunction() // CHECK: [[ZERO:%.*]] = load %struct._class_t*, %struct._class_t** @"OBJC_CLASSLIST_REFERENCES_ // CHECK-NEXT: [[ONE:%.*]] = load i8*, i8** @OBJC_SELECTOR_REFERENCES_ // CHECK-NEXT: [[TWO:%.*]] = bitcast %struct._class_t* [[ZERO]] to i8* -// CHECK-NEXT: call void (i8*, i8*, [[T:%.*]]*, ...) bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*, [[T:%.*]]*, ...)*)(i8* [[TWO]], i8* [[ONE]], [[T:%.*]]* bitcast (%struct.NSConstantString* @_unnamed_cfstring_{{.*}} to [[T:%.*]]*)) +// CHECK-NEXT: call void (i8*, i8*, [[T:%.*]]*, ...) bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*, [[T:%.*]]*, ...)*)(i8* [[TWO]], i8* [[ONE]], [[T:%.*]]* bitcast (%struct.__NSConstantString* @_unnamed_cfstring_{{.*}} to [[T:%.*]]*)) // CHECK-NEXT: ret void diff --git a/test/Modules/Inputs/builtin.h b/test/Modules/Inputs/builtin.h index 7be90177d19..d8779452342 100644 --- a/test/Modules/Inputs/builtin.h +++ b/test/Modules/Inputs/builtin.h @@ -1,3 +1,10 @@ int i; int *p = &i; +#ifdef __OBJC__ +void use_constant_string_builtins(void) { + (void)__builtin___CFStringMakeConstantString(""); + (void)__builtin___NSStringMakeConstantString(""); +} +#endif + diff --git a/test/Modules/builtins.m b/test/Modules/builtins.m index 33d23979ce7..a835039cc60 100644 --- a/test/Modules/builtins.m +++ b/test/Modules/builtins.m @@ -1,3 +1,17 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -fmodules-cache-path=%t -fmodules -fimplicit-module-maps -I %S/Inputs %s -verify + +// RUN: rm -rf %t.pch.cache +// RUN: %clang_cc1 -fmodules-cache-path=%t.pch.cache -fmodules -fimplicit-module-maps -I %S/Inputs -emit-pch -o %t.pch -x objective-c-header %S/Inputs/use-builtin.h +// RUN: %clang_cc1 -fmodules-cache-path=%t.pch.cache -fmodules -fimplicit-module-maps -I %S/Inputs %s -include-pch %t.pch %s -verify + +// expected-no-diagnostics + +void use_constant_string_builtins1(void) { + (void)__builtin___CFStringMakeConstantString(""); + (void)__builtin___NSStringMakeConstantString(""); +} + @import builtin; int foo() { @@ -14,11 +28,7 @@ int baz() { return IS_CONST(0); } -// RUN: rm -rf %t -// RUN: %clang_cc1 -fmodules-cache-path=%t -fmodules -fimplicit-module-maps -I %S/Inputs %s -verify - -// RUN: rm -rf %t.pch.cache -// RUN: %clang_cc1 -fmodules-cache-path=%t.pch.cache -fmodules -fimplicit-module-maps -I %S/Inputs -emit-pch -o %t.pch -x objective-c-header %S/Inputs/use-builtin.h -// RUN: %clang_cc1 -fmodules-cache-path=%t.pch.cache -fmodules -fimplicit-module-maps -I %S/Inputs %s -include-pch %t.pch %s -verify - -// expected-no-diagnostics +void use_constant_string_builtins2(void) { + (void)__builtin___CFStringMakeConstantString(""); + (void)__builtin___NSStringMakeConstantString(""); +} From 0d13c9d9428c8392c29f7c6dee024553404bd0e7 Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Thu, 4 Feb 2016 00:55:24 +0000 Subject: [PATCH 079/742] Fix predefine for __NSConstantString struct type Per review feedback the name was wrong and it can be used outside Objective-C. Unfortunately, making the internal struct visible broke some ASTMatchers tests that assumed that the first record decl would be from user code, rather than a builtin type. I'm worried that this will also affect users' code. So this patch adds a typedef to wrap the internal struct and only makes the typedef visible to namelookup. This is sufficient to allow the ASTReader to merge the decls we need without making the struct itself visible. rdar://problem/24425801 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259734 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/ASTContext.h | 10 ++-- include/clang/Serialization/ASTBitCodes.h | 7 ++- lib/AST/ASTContext.cpp | 52 ++++++++++++------- lib/Sema/Sema.cpp | 9 ++-- lib/Serialization/ASTReader.cpp | 3 ++ lib/Serialization/ASTWriter.cpp | 2 + test/CodeGenObjC/2010-02-01-utf16-with-null.m | 4 +- test/CodeGenObjC/arc-no-arc-exceptions.m | 2 +- test/CodeGenObjC/tentative-cfconstantstring.m | 5 +- test/Modules/Inputs/builtin.h | 3 -- test/Modules/builtins.m | 6 ++- 11 files changed, 64 insertions(+), 39 deletions(-) diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index 1d053411ebc..438e676a979 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -253,8 +253,9 @@ class ASTContext : public RefCountedBase { mutable IdentifierInfo *MakeIntegerSeqName = nullptr; QualType ObjCConstantStringType; - mutable RecordDecl *CFConstantStringTypeDecl; - + mutable RecordDecl *CFConstantStringTagDecl; + mutable TypedefDecl *CFConstantStringTypeDecl; + mutable QualType ObjCSuperType; QualType ObjCNSStringType; @@ -1381,11 +1382,12 @@ class ASTContext : public RefCountedBase { /// if it hasn't yet been built. QualType getRawCFConstantStringType() const { if (CFConstantStringTypeDecl) - return getTagDeclType(CFConstantStringTypeDecl); + return getTypedefType(CFConstantStringTypeDecl); return QualType(); } void setCFConstantStringType(QualType T); - TagDecl *getCFConstantStringDecl() const; + TypedefDecl *getCFConstantStringDecl() const; + RecordDecl *getCFConstantStringTagDecl() const; // This setter/getter represents the ObjC type for an NSConstantString. void setObjCConstantStringInterface(ObjCInterfaceDecl *Decl); diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index 4bb1f5eacfe..910c5770407 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -988,15 +988,18 @@ namespace clang { /// \brief The internal '__make_integer_seq' template. PREDEF_DECL_MAKE_INTEGER_SEQ_ID = 13, - /// \brief The internal '__NSConstantString' type. + /// \brief The internal '__NSConstantString' typedef. PREDEF_DECL_CF_CONSTANT_STRING_ID = 14, + + /// \brief The internal '__NSConstantString' tag type. + PREDEF_DECL_CF_CONSTANT_STRING_TAG_ID = 15, }; /// \brief The number of declaration IDs that are predefined. /// /// For more information about predefined declarations, see the /// \c PredefinedDeclIDs type and the PREDEF_DECL_*_ID constants. - const unsigned int NUM_PREDEF_DECL_IDS = 15; + const unsigned int NUM_PREDEF_DECL_IDS = 16; /// \brief Record code for a list of local redeclarations of a declaration. const unsigned int LOCAL_REDECLARATIONS = 50; diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index fc5ff8668e9..06fb46e2f7f 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -738,12 +738,13 @@ ASTContext::ASTContext(LangOptions &LOpts, SourceManager &SM, BuiltinVaListDecl(nullptr), BuiltinMSVaListDecl(nullptr), ObjCIdDecl(nullptr), ObjCSelDecl(nullptr), ObjCClassDecl(nullptr), ObjCProtocolClassDecl(nullptr), BOOLDecl(nullptr), - CFConstantStringTypeDecl(nullptr), ObjCInstanceTypeDecl(nullptr), - FILEDecl(nullptr), jmp_bufDecl(nullptr), sigjmp_bufDecl(nullptr), - ucontext_tDecl(nullptr), BlockDescriptorType(nullptr), - BlockDescriptorExtendedType(nullptr), cudaConfigureCallDecl(nullptr), - FirstLocalImport(), LastLocalImport(), ExternCContext(nullptr), - MakeIntegerSeqDecl(nullptr), SourceMgr(SM), LangOpts(LOpts), + CFConstantStringTagDecl(nullptr), CFConstantStringTypeDecl(nullptr), + ObjCInstanceTypeDecl(nullptr), FILEDecl(nullptr), jmp_bufDecl(nullptr), + sigjmp_bufDecl(nullptr), ucontext_tDecl(nullptr), + BlockDescriptorType(nullptr), BlockDescriptorExtendedType(nullptr), + cudaConfigureCallDecl(nullptr), FirstLocalImport(), LastLocalImport(), + ExternCContext(nullptr), MakeIntegerSeqDecl(nullptr), SourceMgr(SM), + LangOpts(LOpts), SanitizerBL(new SanitizerBlacklist(LangOpts.SanitizerBlacklistFiles, SM)), AddrSpaceMap(nullptr), Target(nullptr), AuxTarget(nullptr), PrintingPolicy(LOpts), Idents(idents), Selectors(sels), @@ -4868,12 +4869,12 @@ int ASTContext::getIntegerTypeOrder(QualType LHS, QualType RHS) const { return 1; } -TagDecl *ASTContext::getCFConstantStringDecl() const { +TypedefDecl *ASTContext::getCFConstantStringDecl() const { if (!CFConstantStringTypeDecl) { - // This type is designed to be compatible with NSConstantString, but cannot - // use the same name, since NSConstantString is an interface. - CFConstantStringTypeDecl = buildImplicitRecord("__NSConstantString"); - CFConstantStringTypeDecl->startDefinition(); + assert(!CFConstantStringTagDecl && + "tag and typedef should be initialized together"); + CFConstantStringTagDecl = buildImplicitRecord("__NSConstantString_tag"); + CFConstantStringTagDecl->startDefinition(); QualType FieldTypes[4]; @@ -4888,7 +4889,7 @@ TagDecl *ASTContext::getCFConstantStringDecl() const { // Create fields for (unsigned i = 0; i < 4; ++i) { - FieldDecl *Field = FieldDecl::Create(*this, CFConstantStringTypeDecl, + FieldDecl *Field = FieldDecl::Create(*this, CFConstantStringTagDecl, SourceLocation(), SourceLocation(), nullptr, FieldTypes[i], /*TInfo=*/nullptr, @@ -4896,18 +4897,29 @@ TagDecl *ASTContext::getCFConstantStringDecl() const { /*Mutable=*/false, ICIS_NoInit); Field->setAccess(AS_public); - CFConstantStringTypeDecl->addDecl(Field); + CFConstantStringTagDecl->addDecl(Field); } - CFConstantStringTypeDecl->completeDefinition(); + CFConstantStringTagDecl->completeDefinition(); + // This type is designed to be compatible with NSConstantString, but cannot + // use the same name, since NSConstantString is an interface. + auto tagType = getTagDeclType(CFConstantStringTagDecl); + CFConstantStringTypeDecl = + buildImplicitTypedef(tagType, "__NSConstantString"); } return CFConstantStringTypeDecl; } +RecordDecl *ASTContext::getCFConstantStringTagDecl() const { + if (!CFConstantStringTagDecl) + getCFConstantStringDecl(); // Build the tag and the typedef. + return CFConstantStringTagDecl; +} + // getCFConstantStringType - Return the type used for constant CFStrings. QualType ASTContext::getCFConstantStringType() const { - return getTagDeclType(getCFConstantStringDecl()); + return getTypedefType(getCFConstantStringDecl()); } QualType ASTContext::getObjCSuperType() const { @@ -4920,9 +4932,13 @@ QualType ASTContext::getObjCSuperType() const { } void ASTContext::setCFConstantStringType(QualType T) { - const RecordType *Rec = T->getAs(); - assert(Rec && "Invalid CFConstantStringType"); - CFConstantStringTypeDecl = Rec->getDecl(); + const TypedefType *TD = T->getAs(); + assert(TD && "Invalid CFConstantStringType"); + CFConstantStringTypeDecl = cast(TD->getDecl()); + auto TagType = + CFConstantStringTypeDecl->getUnderlyingType()->getAs(); + assert(TagType && "Invalid CFConstantStringType"); + CFConstantStringTagDecl = TagType->getDecl(); } QualType ASTContext::getBlockDescriptorType() const { diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index 0119ddac1fa..34261f2556c 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -189,12 +189,13 @@ void Sema::Initialize() { DeclarationName Protocol = &Context.Idents.get("Protocol"); if (IdResolver.begin(Protocol) == IdResolver.end()) PushOnScopeChains(Context.getObjCProtocolDecl(), TUScope); - - DeclarationName ConstantString = &Context.Idents.get("NSConstantString"); - if (IdResolver.begin(ConstantString) == IdResolver.end()) - PushOnScopeChains(Context.getCFConstantStringDecl(), TUScope); } + // Create the internal type for the *StringMakeConstantString builtins. + DeclarationName ConstantString = &Context.Idents.get("__NSConstantString"); + if (IdResolver.begin(ConstantString) == IdResolver.end()) + PushOnScopeChains(Context.getCFConstantStringDecl(), TUScope); + // Initialize Microsoft "predefined C++ types". if (getLangOpts().MSVCCompat) { if (getLangOpts().CPlusPlus && diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index b7c869c23ba..b61265bccb8 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -6447,6 +6447,9 @@ static Decl *getPredefinedDecl(ASTContext &Context, PredefinedDeclIDs ID) { case PREDEF_DECL_CF_CONSTANT_STRING_ID: return Context.getCFConstantStringDecl(); + + case PREDEF_DECL_CF_CONSTANT_STRING_TAG_ID: + return Context.getCFConstantStringTagDecl(); } llvm_unreachable("PredefinedDeclIDs unknown enum value"); } diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index a590e188eca..0b28a7aa673 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -4154,6 +4154,8 @@ uint64_t ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot, PREDEF_DECL_MAKE_INTEGER_SEQ_ID); RegisterPredefDecl(Context.CFConstantStringTypeDecl, PREDEF_DECL_CF_CONSTANT_STRING_ID); + RegisterPredefDecl(Context.CFConstantStringTagDecl, + PREDEF_DECL_CF_CONSTANT_STRING_TAG_ID); // Build a record containing all of the tentative definitions in this file, in // TentativeDefinitions order. Generally, this record will be empty for diff --git a/test/CodeGenObjC/2010-02-01-utf16-with-null.m b/test/CodeGenObjC/2010-02-01-utf16-with-null.m index 856ac9ac97b..7c103f2ba6d 100644 --- a/test/CodeGenObjC/2010-02-01-utf16-with-null.m +++ b/test/CodeGenObjC/2010-02-01-utf16-with-null.m @@ -2,6 +2,6 @@ // rdar://7589850 // CHECK: @.str = private unnamed_addr constant [9 x i16] [i16 103, i16 111, i16 111, i16 100, i16 0, i16 98, i16 121, i16 101, i16 0], section "__TEXT,__ustring", align 2 -// CHECK: @_unnamed_cfstring_ = private constant %struct.__NSConstantString { i32* getelementptr inbounds ([0 x i32], [0 x i32]* @__CFConstantStringClassReference, i32 0, i32 0), i32 2000, i8* bitcast ([9 x i16]* @.str to i8*), i32 8 }, section "__DATA,__cfstring" -// CHECK: @P = global i8* bitcast (%struct.__NSConstantString* @_unnamed_cfstring_ to i8*), align 4 +// CHECK: @_unnamed_cfstring_ = private constant %struct.__NSConstantString_tag { i32* getelementptr inbounds ([0 x i32], [0 x i32]* @__CFConstantStringClassReference, i32 0, i32 0), i32 2000, i8* bitcast ([9 x i16]* @.str to i8*), i32 8 }, section "__DATA,__cfstring" +// CHECK: @P = global i8* bitcast (%struct.__NSConstantString_tag* @_unnamed_cfstring_ to i8*), align 4 void *P = @"good\0bye"; diff --git a/test/CodeGenObjC/arc-no-arc-exceptions.m b/test/CodeGenObjC/arc-no-arc-exceptions.m index 30424e7fb36..f147b64c484 100644 --- a/test/CodeGenObjC/arc-no-arc-exceptions.m +++ b/test/CodeGenObjC/arc-no-arc-exceptions.m @@ -34,7 +34,7 @@ void test1(id x) { void NSLog(id, ...); // CHECK-LABEL: define void @test2( -// CHECK: invoke void (i8*, ...) @NSLog(i8* bitcast (%struct.__NSConstantString* @_unnamed_cfstring_ to i8*), i32* %{{.*}}) +// CHECK: invoke void (i8*, ...) @NSLog(i8* bitcast (%struct.__NSConstantString_tag* @_unnamed_cfstring_ to i8*), i32* %{{.*}}) // CHECK: to label %{{.*}} unwind label %{{.*}}, !clang.arc.no_objc_arc_exceptions ! // NO-METADATA-LABEL: define void @test2( // NO-METADATA-NOT: !clang.arc.no_objc_arc_exceptions diff --git a/test/CodeGenObjC/tentative-cfconstantstring.m b/test/CodeGenObjC/tentative-cfconstantstring.m index 0c692c37c95..9ff1a0a2fce 100644 --- a/test/CodeGenObjC/tentative-cfconstantstring.m +++ b/test/CodeGenObjC/tentative-cfconstantstring.m @@ -32,12 +32,11 @@ -(void)someMethod { @end // CHECK: @__CFConstantStringClassReference = common global [24 x i32] zeroinitializer, align 16 -// CHECK: @_unnamed_cfstring_{{.*}} = private constant %struct.__NSConstantString { i32* getelementptr inbounds ([24 x i32], [24 x i32]* @__CFConstantStringClassReference, i32 0, i32 0) +// CHECK: @_unnamed_cfstring_{{.*}} = private constant %struct.__NSConstantString_tag { i32* getelementptr inbounds ([24 x i32], [24 x i32]* @__CFConstantStringClassReference, i32 0, i32 0) // CHECK-LABEL: define internal void @_inlineFunction() // CHECK: [[ZERO:%.*]] = load %struct._class_t*, %struct._class_t** @"OBJC_CLASSLIST_REFERENCES_ // CHECK-NEXT: [[ONE:%.*]] = load i8*, i8** @OBJC_SELECTOR_REFERENCES_ // CHECK-NEXT: [[TWO:%.*]] = bitcast %struct._class_t* [[ZERO]] to i8* -// CHECK-NEXT: call void (i8*, i8*, [[T:%.*]]*, ...) bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*, [[T:%.*]]*, ...)*)(i8* [[TWO]], i8* [[ONE]], [[T:%.*]]* bitcast (%struct.__NSConstantString* @_unnamed_cfstring_{{.*}} to [[T:%.*]]*)) +// CHECK-NEXT: call void (i8*, i8*, [[T:%.*]]*, ...) bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*, [[T:%.*]]*, ...)*)(i8* [[TWO]], i8* [[ONE]], [[T:%.*]]* bitcast (%struct.__NSConstantString_tag* @_unnamed_cfstring_{{.*}} to [[T:%.*]]*)) // CHECK-NEXT: ret void - diff --git a/test/Modules/Inputs/builtin.h b/test/Modules/Inputs/builtin.h index d8779452342..4717ff2a52a 100644 --- a/test/Modules/Inputs/builtin.h +++ b/test/Modules/Inputs/builtin.h @@ -1,10 +1,7 @@ int i; int *p = &i; -#ifdef __OBJC__ void use_constant_string_builtins(void) { (void)__builtin___CFStringMakeConstantString(""); (void)__builtin___NSStringMakeConstantString(""); } -#endif - diff --git a/test/Modules/builtins.m b/test/Modules/builtins.m index a835039cc60..2480e6379cc 100644 --- a/test/Modules/builtins.m +++ b/test/Modules/builtins.m @@ -1,5 +1,7 @@ // RUN: rm -rf %t // RUN: %clang_cc1 -fmodules-cache-path=%t -fmodules -fimplicit-module-maps -I %S/Inputs %s -verify +// RUN: %clang_cc1 -fmodules-cache-path=%t -fmodules -fimplicit-module-maps -I %S/Inputs -x c %s -verify +// RUN: %clang_cc1 -fmodules-cache-path=%t -fmodules -fimplicit-module-maps -I %S/Inputs -x objective-c++ %s -verify // RUN: rm -rf %t.pch.cache // RUN: %clang_cc1 -fmodules-cache-path=%t.pch.cache -fmodules -fimplicit-module-maps -I %S/Inputs -emit-pch -o %t.pch -x objective-c-header %S/Inputs/use-builtin.h @@ -12,13 +14,13 @@ void use_constant_string_builtins1(void) { (void)__builtin___NSStringMakeConstantString(""); } -@import builtin; +#include "builtin.h" int foo() { return __builtin_object_size(p, 0); } -@import builtin.sub; +#include "builtin_sub.h" int bar() { return __builtin_object_size(p, 0); From b993b2e9161e736791dcb03219357f8195afcead Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Fri, 5 Feb 2016 01:10:05 +0000 Subject: [PATCH 080/742] Don't synthesize an ImportDecl for a module named in -fmodule-implementation-of When building a PCH with modules enabled this import would assert in the ASTWriter and (if assertions were disabled) sometimes crash the compiler that loaded the resulting PCH when trying to lookup the submodule ID. rdar://problem/24137448 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259859 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/SemaDecl.cpp | 8 +++++++- test/Modules/Inputs/category_right.h | 1 + test/Modules/objc-categories.m | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 7cc669c156d..f8de19b9681 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -14715,9 +14715,15 @@ void Sema::ActOnModuleInclude(SourceLocation DirectiveLoc, Module *Mod) { TUKind == TU_Module && getSourceManager().isWrittenInMainFile(DirectiveLoc); + // Similarly, if this module is specified by -fmodule-implementation-of + // don't actually synthesize an illegal module import. + bool ShouldAddImport = !IsInModuleIncludes && + (getLangOpts().ImplementationOfModule.empty() || + getLangOpts().ImplementationOfModule != Mod->getTopLevelModuleName()); + // If this module import was due to an inclusion directive, create an // implicit import declaration to capture it in the AST. - if (!IsInModuleIncludes) { + if (ShouldAddImport) { TranslationUnitDecl *TU = getASTContext().getTranslationUnitDecl(); ImportDecl *ImportD = ImportDecl::CreateImplicit(getASTContext(), TU, DirectiveLoc, Mod, diff --git a/test/Modules/Inputs/category_right.h b/test/Modules/Inputs/category_right.h index 3c83624c761..d8dedf888d1 100644 --- a/test/Modules/Inputs/category_right.h +++ b/test/Modules/Inputs/category_right.h @@ -1,4 +1,5 @@ @import category_top; +#import "category_right_sub.h" @interface Foo(Right1) -(void)right1; diff --git a/test/Modules/objc-categories.m b/test/Modules/objc-categories.m index e8549fabb50..42baf352fbf 100644 --- a/test/Modules/objc-categories.m +++ b/test/Modules/objc-categories.m @@ -9,7 +9,7 @@ @import category_bottom; // expected-note@Inputs/category_left.h:14 {{previous definition}} -// expected-warning@Inputs/category_right.h:11 {{duplicate definition of category}} +// expected-warning@Inputs/category_right.h:12 {{duplicate definition of category}} // expected-note@Inputs/category_top.h:1 {{receiver is instance of class declared here}} @interface Foo(Source) From 798fd4111eaa8febc2bb337b7e7ef254fb4633da Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 27 Jan 2016 18:32:30 +0000 Subject: [PATCH 081/742] Emit calls to objc_unsafeClaimAutoreleasedReturnValue when reclaiming a call result in order to ignore it or assign it to an __unsafe_unretained variable. This avoids adding an unwanted retain/release pair when the return value is not actually returned autoreleased (e.g. when it is returned from a nonatomic getter or a typical collection accessor). This runtime function is only available on the latest Apple OS releases; the backwards-compatibility story is that you don't get the optimization unless your deployment target is recent enough. Sorry. rdar://20530049 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258962 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/ObjCRuntime.h | 17 + lib/CodeGen/CGDecl.cpp | 3 +- lib/CodeGen/CGExprScalar.cpp | 18 +- lib/CodeGen/CGObjC.cpp | 595 +++++++++++++++++++++-------- lib/CodeGen/CodeGenFunction.h | 6 + lib/CodeGen/CodeGenModule.h | 3 + test/CodeGenObjC/arc-unsafeclaim.m | 231 +++++++++++ 7 files changed, 702 insertions(+), 171 deletions(-) create mode 100644 test/CodeGenObjC/arc-unsafeclaim.m diff --git a/include/clang/Basic/ObjCRuntime.h b/include/clang/Basic/ObjCRuntime.h index cf51b146b1d..6975b6c9bb9 100644 --- a/include/clang/Basic/ObjCRuntime.h +++ b/include/clang/Basic/ObjCRuntime.h @@ -308,6 +308,23 @@ class ObjCRuntime { } } + /// Is objc_unsafeClaimAutoreleasedReturnValue available? + bool hasARCUnsafeClaimAutoreleasedReturnValue() const { + switch (getKind()) { + case MacOSX: + return getVersion() >= VersionTuple(10, 11); + case iOS: + return getVersion() >= VersionTuple(9); + case WatchOS: + return getVersion() >= VersionTuple(2); + case GNUstep: + return false; + + default: + return false; + } + } + /// \brief Try to parse an Objective-C runtime specification from the given /// string. /// diff --git a/lib/CodeGen/CGDecl.cpp b/lib/CodeGen/CGDecl.cpp index b78e80d79dd..cdf8f615cb1 100644 --- a/lib/CodeGen/CGDecl.cpp +++ b/lib/CodeGen/CGDecl.cpp @@ -715,8 +715,7 @@ void CodeGenFunction::EmitScalarInit(const Expr *init, const ValueDecl *D, llvm_unreachable("present but none"); case Qualifiers::OCL_ExplicitNone: - // nothing to do - value = EmitScalarExpr(init); + value = EmitARCUnsafeUnretainedScalarExpr(init); break; case Qualifiers::OCL_Strong: { diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp index 268e7967b80..5b39b5d4399 100644 --- a/lib/CodeGen/CGExprScalar.cpp +++ b/lib/CodeGen/CGExprScalar.cpp @@ -1366,8 +1366,9 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) { QualType DestTy = CE->getType(); CastKind Kind = CE->getCastKind(); - if (!DestTy->isVoidType()) - TestAndClearIgnoreResultAssign(); + // These cases are generally not written to ignore the result of + // evaluating their sub-expressions, so we clear this now. + bool Ignored = TestAndClearIgnoreResultAssign(); // Since almost all cast kinds apply to scalars, this switch doesn't have // a default case, so the compiler will warn on a missing case. The cases @@ -1494,11 +1495,8 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) { return CGF.EmitARCRetainScalarExpr(E); case CK_ARCConsumeObject: return CGF.EmitObjCConsumeObject(E->getType(), Visit(E)); - case CK_ARCReclaimReturnedObject: { - llvm::Value *value = Visit(E); - value = CGF.EmitARCRetainAutoreleasedReturnValue(value); - return CGF.EmitObjCConsumeObject(E->getType(), value); - } + case CK_ARCReclaimReturnedObject: + return CGF.EmitARCReclaimReturnedObject(E, /*allowUnsafe*/ Ignored); case CK_ARCExtendBlockObject: return CGF.EmitARCExtendBlockObject(E); @@ -2993,15 +2991,17 @@ Value *ScalarExprEmitter::VisitBinAssign(const BinaryOperator *E) { std::tie(LHS, RHS) = CGF.EmitARCStoreAutoreleasing(E); break; + case Qualifiers::OCL_ExplicitNone: + std::tie(LHS, RHS) = CGF.EmitARCStoreUnsafeUnretained(E, Ignore); + break; + case Qualifiers::OCL_Weak: RHS = Visit(E->getRHS()); LHS = EmitCheckedLValue(E->getLHS(), CodeGenFunction::TCK_Store); RHS = CGF.EmitARCStoreWeak(LHS.getAddress(), RHS, Ignore); break; - // No reason to do any of these differently. case Qualifiers::OCL_None: - case Qualifiers::OCL_ExplicitNone: // __block variables need to have the rhs evaluated first, plus // this should improve codegen just a little. RHS = Visit(E->getRHS()); diff --git a/lib/CodeGen/CGObjC.cpp b/lib/CodeGen/CGObjC.cpp index 2d5991b71fc..989d911a0cb 100644 --- a/lib/CodeGen/CGObjC.cpp +++ b/lib/CodeGen/CGObjC.cpp @@ -1980,20 +1980,14 @@ llvm::Value *CodeGenFunction::EmitARCRetainBlock(llvm::Value *value, return result; } -/// Retain the given object which is the result of a function call. -/// call i8* \@objc_retainAutoreleasedReturnValue(i8* %value) -/// -/// Yes, this function name is one character away from a different -/// call with completely different semantics. -llvm::Value * -CodeGenFunction::EmitARCRetainAutoreleasedReturnValue(llvm::Value *value) { +static void emitAutoreleasedReturnValueMarker(CodeGenFunction &CGF) { // Fetch the void(void) inline asm which marks that we're going to - // retain the autoreleased return value. + // do something with the autoreleased return value. llvm::InlineAsm *&marker - = CGM.getObjCEntrypoints().retainAutoreleasedReturnValueMarker; + = CGF.CGM.getObjCEntrypoints().retainAutoreleasedReturnValueMarker; if (!marker) { StringRef assembly - = CGM.getTargetCodeGenInfo() + = CGF.CGM.getTargetCodeGenInfo() .getARCRetainAutoreleasedReturnValueMarker(); // If we have an empty assembly string, there's nothing to do. @@ -2001,9 +1995,9 @@ CodeGenFunction::EmitARCRetainAutoreleasedReturnValue(llvm::Value *value) { // Otherwise, at -O0, build an inline asm that we're going to call // in a moment. - } else if (CGM.getCodeGenOpts().OptimizationLevel == 0) { + } else if (CGF.CGM.getCodeGenOpts().OptimizationLevel == 0) { llvm::FunctionType *type = - llvm::FunctionType::get(VoidTy, /*variadic*/false); + llvm::FunctionType::get(CGF.VoidTy, /*variadic*/false); marker = llvm::InlineAsm::get(type, assembly, "", /*sideeffects*/ true); @@ -2012,25 +2006,50 @@ CodeGenFunction::EmitARCRetainAutoreleasedReturnValue(llvm::Value *value) { // optimizer to pick up. } else { llvm::NamedMDNode *metadata = - CGM.getModule().getOrInsertNamedMetadata( + CGF.CGM.getModule().getOrInsertNamedMetadata( "clang.arc.retainAutoreleasedReturnValueMarker"); assert(metadata->getNumOperands() <= 1); if (metadata->getNumOperands() == 0) { - metadata->addOperand(llvm::MDNode::get( - getLLVMContext(), llvm::MDString::get(getLLVMContext(), assembly))); + auto &ctx = CGF.getLLVMContext(); + metadata->addOperand(llvm::MDNode::get(ctx, + llvm::MDString::get(ctx, assembly))); } } } // Call the marker asm if we made one, which we do only at -O0. if (marker) - Builder.CreateCall(marker); + CGF.Builder.CreateCall(marker); +} +/// Retain the given object which is the result of a function call. +/// call i8* \@objc_retainAutoreleasedReturnValue(i8* %value) +/// +/// Yes, this function name is one character away from a different +/// call with completely different semantics. +llvm::Value * +CodeGenFunction::EmitARCRetainAutoreleasedReturnValue(llvm::Value *value) { + emitAutoreleasedReturnValueMarker(*this); return emitARCValueOperation(*this, value, - CGM.getObjCEntrypoints().objc_retainAutoreleasedReturnValue, + CGM.getObjCEntrypoints().objc_retainAutoreleasedReturnValue, "objc_retainAutoreleasedReturnValue"); } +/// Claim a possibly-autoreleased return value at +0. This is only +/// valid to do in contexts which do not rely on the retain to keep +/// the object valid for for all of its uses; for example, when +/// the value is ignored, or when it is being assigned to an +/// __unsafe_unretained variable. +/// +/// call i8* \@objc_unsafeClaimAutoreleasedReturnValue(i8* %value) +llvm::Value * +CodeGenFunction::EmitARCUnsafeClaimAutoreleasedReturnValue(llvm::Value *value) { + emitAutoreleasedReturnValueMarker(*this); + return emitARCValueOperation(*this, value, + CGM.getObjCEntrypoints().objc_unsafeClaimAutoreleasedReturnValue, + "objc_unsafeClaimAutoreleasedReturnValue"); +} + /// Release the given object. /// call void \@objc_release(i8* %value) void CodeGenFunction::EmitARCRelease(llvm::Value *value, @@ -2446,25 +2465,22 @@ static TryEmitResult tryEmitARCRetainLoadOfScalar(CodeGenFunction &CGF, return tryEmitARCRetainLoadOfScalar(CGF, CGF.EmitLValue(e), type); } -static llvm::Value *emitARCRetainAfterCall(CodeGenFunction &CGF, - llvm::Value *value); +typedef llvm::function_ref + ValueTransform; -/// Given that the given expression is some sort of call (which does -/// not return retained), emit a retain following it. -static llvm::Value *emitARCRetainCall(CodeGenFunction &CGF, const Expr *e) { - llvm::Value *value = CGF.EmitScalarExpr(e); - return emitARCRetainAfterCall(CGF, value); -} - -static llvm::Value *emitARCRetainAfterCall(CodeGenFunction &CGF, - llvm::Value *value) { +/// Insert code immediately after a call. +static llvm::Value *emitARCOperationAfterCall(CodeGenFunction &CGF, + llvm::Value *value, + ValueTransform doAfterCall, + ValueTransform doFallback) { if (llvm::CallInst *call = dyn_cast(value)) { CGBuilderTy::InsertPoint ip = CGF.Builder.saveIP(); // Place the retain immediately following the call. CGF.Builder.SetInsertPoint(call->getParent(), ++llvm::BasicBlock::iterator(call)); - value = CGF.EmitARCRetainAutoreleasedReturnValue(value); + value = doAfterCall(CGF, value); CGF.Builder.restoreIP(ip); return value; @@ -2474,7 +2490,7 @@ static llvm::Value *emitARCRetainAfterCall(CodeGenFunction &CGF, // Place the retain at the beginning of the normal destination block. llvm::BasicBlock *BB = invoke->getNormalDest(); CGF.Builder.SetInsertPoint(BB, BB->begin()); - value = CGF.EmitARCRetainAutoreleasedReturnValue(value); + value = doAfterCall(CGF, value); CGF.Builder.restoreIP(ip); return value; @@ -2483,7 +2499,7 @@ static llvm::Value *emitARCRetainAfterCall(CodeGenFunction &CGF, // the operand. } else if (llvm::BitCastInst *bitcast = dyn_cast(value)) { llvm::Value *operand = bitcast->getOperand(0); - operand = emitARCRetainAfterCall(CGF, operand); + operand = emitARCOperationAfterCall(CGF, operand, doAfterCall, doFallback); bitcast->setOperand(0, operand); return bitcast; @@ -2491,7 +2507,46 @@ static llvm::Value *emitARCRetainAfterCall(CodeGenFunction &CGF, } else { // Retain using the non-block variant: we never need to do a copy // of a block that's been returned to us. - return CGF.EmitARCRetainNonBlock(value); + return doFallback(CGF, value); + } +} + +/// Given that the given expression is some sort of call (which does +/// not return retained), emit a retain following it. +static llvm::Value *emitARCRetainCallResult(CodeGenFunction &CGF, + const Expr *e) { + llvm::Value *value = CGF.EmitScalarExpr(e); + return emitARCOperationAfterCall(CGF, value, + [](CodeGenFunction &CGF, llvm::Value *value) { + return CGF.EmitARCRetainAutoreleasedReturnValue(value); + }, + [](CodeGenFunction &CGF, llvm::Value *value) { + return CGF.EmitARCRetainNonBlock(value); + }); +} + +/// Given that the given expression is some sort of call (which does +/// not return retained), perform an unsafeClaim following it. +static llvm::Value *emitARCUnsafeClaimCallResult(CodeGenFunction &CGF, + const Expr *e) { + llvm::Value *value = CGF.EmitScalarExpr(e); + return emitARCOperationAfterCall(CGF, value, + [](CodeGenFunction &CGF, llvm::Value *value) { + return CGF.EmitARCUnsafeClaimAutoreleasedReturnValue(value); + }, + [](CodeGenFunction &CGF, llvm::Value *value) { + return value; + }); +} + +llvm::Value *CodeGenFunction::EmitARCReclaimReturnedObject(const Expr *E, + bool allowUnsafeClaim) { + if (allowUnsafeClaim && + CGM.getLangOpts().ObjCRuntime.hasARCUnsafeClaimAutoreleasedReturnValue()) { + return emitARCUnsafeClaimCallResult(*this, E); + } else { + llvm::Value *value = emitARCRetainCallResult(*this, E); + return EmitObjCConsumeObject(E->getType(), value); } } @@ -2531,17 +2586,52 @@ static bool shouldEmitSeparateBlockRetain(const Expr *e) { return true; } -/// Try to emit a PseudoObjectExpr at +1. +namespace { +/// A CRTP base class for emitting expressions of retainable object +/// pointer type in ARC. +template class ARCExprEmitter { +protected: + CodeGenFunction &CGF; + Impl &asImpl() { return *static_cast(this); } + + ARCExprEmitter(CodeGenFunction &CGF) : CGF(CGF) {} + +public: + Result visit(const Expr *e); + Result visitCastExpr(const CastExpr *e); + Result visitPseudoObjectExpr(const PseudoObjectExpr *e); + Result visitBinaryOperator(const BinaryOperator *e); + Result visitBinAssign(const BinaryOperator *e); + Result visitBinAssignUnsafeUnretained(const BinaryOperator *e); + Result visitBinAssignAutoreleasing(const BinaryOperator *e); + Result visitBinAssignWeak(const BinaryOperator *e); + Result visitBinAssignStrong(const BinaryOperator *e); + + // Minimal implementation: + // Result visitLValueToRValue(const Expr *e) + // Result visitConsumeObject(const Expr *e) + // Result visitExtendBlockObject(const Expr *e) + // Result visitReclaimReturnedObject(const Expr *e) + // Result visitCall(const Expr *e) + // Result visitExpr(const Expr *e) + // + // Result emitBitCast(Result result, llvm::Type *resultType) + // llvm::Value *getValueOfResult(Result result) +}; +} + +/// Try to emit a PseudoObjectExpr under special ARC rules. /// /// This massively duplicates emitPseudoObjectRValue. -static TryEmitResult tryEmitARCRetainPseudoObject(CodeGenFunction &CGF, - const PseudoObjectExpr *E) { +template +Result +ARCExprEmitter::visitPseudoObjectExpr(const PseudoObjectExpr *E) { SmallVector opaques; // Find the result expression. const Expr *resultExpr = E->getResultExpr(); assert(resultExpr); - TryEmitResult result; + Result result; for (PseudoObjectExpr::const_semantics_iterator i = E->semantics_begin(), e = E->semantics_end(); i != e; ++i) { @@ -2557,8 +2647,9 @@ static TryEmitResult tryEmitARCRetainPseudoObject(CodeGenFunction &CGF, // expression, try to evaluate the source as +1. if (ov == resultExpr) { assert(!OVMA::shouldBindAsLValue(ov)); - result = tryEmitARCRetainScalarExpr(CGF, ov->getSourceExpr()); - opaqueData = OVMA::bind(CGF, ov, RValue::get(result.getPointer())); + result = asImpl().visit(ov->getSourceExpr()); + opaqueData = OVMA::bind(CGF, ov, + RValue::get(asImpl().getValueOfResult(result))); // Otherwise, just bind it. } else { @@ -2569,7 +2660,7 @@ static TryEmitResult tryEmitARCRetainPseudoObject(CodeGenFunction &CGF, // Otherwise, if the expression is the result, evaluate it // and remember the result. } else if (semantic == resultExpr) { - result = tryEmitARCRetainScalarExpr(CGF, semantic); + result = asImpl().visit(semantic); // Otherwise, evaluate the expression in an ignored context. } else { @@ -2584,146 +2675,240 @@ static TryEmitResult tryEmitARCRetainPseudoObject(CodeGenFunction &CGF, return result; } -static TryEmitResult -tryEmitARCRetainScalarExpr(CodeGenFunction &CGF, const Expr *e) { +template +Result ARCExprEmitter::visitCastExpr(const CastExpr *e) { + switch (e->getCastKind()) { + + // No-op casts don't change the type, so we just ignore them. + case CK_NoOp: + return asImpl().visit(e->getSubExpr()); + + // These casts can change the type. + case CK_CPointerToObjCPointerCast: + case CK_BlockPointerToObjCPointerCast: + case CK_AnyPointerToBlockPointerCast: + case CK_BitCast: { + llvm::Type *resultType = CGF.ConvertType(e->getType()); + assert(e->getSubExpr()->getType()->hasPointerRepresentation()); + Result result = asImpl().visit(e->getSubExpr()); + return asImpl().emitBitCast(result, resultType); + } + + // Handle some casts specially. + case CK_LValueToRValue: + return asImpl().visitLValueToRValue(e->getSubExpr()); + case CK_ARCConsumeObject: + return asImpl().visitConsumeObject(e->getSubExpr()); + case CK_ARCExtendBlockObject: + return asImpl().visitExtendBlockObject(e->getSubExpr()); + case CK_ARCReclaimReturnedObject: + return asImpl().visitReclaimReturnedObject(e->getSubExpr()); + + // Otherwise, use the default logic. + default: + return asImpl().visitExpr(e); + } +} + +template +Result +ARCExprEmitter::visitBinaryOperator(const BinaryOperator *e) { + switch (e->getOpcode()) { + case BO_Comma: + CGF.EmitIgnoredExpr(e->getLHS()); + CGF.EnsureInsertPoint(); + return asImpl().visit(e->getRHS()); + + case BO_Assign: + return asImpl().visitBinAssign(e); + + default: + return asImpl().visitExpr(e); + } +} + +template +Result ARCExprEmitter::visitBinAssign(const BinaryOperator *e) { + switch (e->getLHS()->getType().getObjCLifetime()) { + case Qualifiers::OCL_ExplicitNone: + return asImpl().visitBinAssignUnsafeUnretained(e); + + case Qualifiers::OCL_Weak: + return asImpl().visitBinAssignWeak(e); + + case Qualifiers::OCL_Autoreleasing: + return asImpl().visitBinAssignAutoreleasing(e); + + case Qualifiers::OCL_Strong: + return asImpl().visitBinAssignStrong(e); + + case Qualifiers::OCL_None: + return asImpl().visitExpr(e); + } + llvm_unreachable("bad ObjC ownership qualifier"); +} + +/// The default rule for __unsafe_unretained emits the RHS recursively, +/// stores into the unsafe variable, and propagates the result outward. +template +Result ARCExprEmitter:: + visitBinAssignUnsafeUnretained(const BinaryOperator *e) { + // Recursively emit the RHS. + // For __block safety, do this before emitting the LHS. + Result result = asImpl().visit(e->getRHS()); + + // Perform the store. + LValue lvalue = + CGF.EmitCheckedLValue(e->getLHS(), CodeGenFunction::TCK_Store); + CGF.EmitStoreThroughLValue(RValue::get(asImpl().getValueOfResult(result)), + lvalue); + + return result; +} + +template +Result +ARCExprEmitter::visitBinAssignAutoreleasing(const BinaryOperator *e) { + return asImpl().visitExpr(e); +} + +template +Result +ARCExprEmitter::visitBinAssignWeak(const BinaryOperator *e) { + return asImpl().visitExpr(e); +} + +template +Result +ARCExprEmitter::visitBinAssignStrong(const BinaryOperator *e) { + return asImpl().visitExpr(e); +} + +/// The general expression-emission logic. +template +Result ARCExprEmitter::visit(const Expr *e) { // We should *never* see a nested full-expression here, because if // we fail to emit at +1, our caller must not retain after we close - // out the full-expression. + // out the full-expression. This isn't as important in the unsafe + // emitter. assert(!isa(e)); - // The desired result type, if it differs from the type of the - // ultimate opaque expression. - llvm::Type *resultType = nullptr; - - while (true) { - e = e->IgnoreParens(); - - // There's a break at the end of this if-chain; anything - // that wants to keep looping has to explicitly continue. - if (const CastExpr *ce = dyn_cast(e)) { - switch (ce->getCastKind()) { - // No-op casts don't change the type, so we just ignore them. - case CK_NoOp: - e = ce->getSubExpr(); - continue; - - case CK_LValueToRValue: { - TryEmitResult loadResult - = tryEmitARCRetainLoadOfScalar(CGF, ce->getSubExpr()); - if (resultType) { - llvm::Value *value = loadResult.getPointer(); - value = CGF.Builder.CreateBitCast(value, resultType); - loadResult.setPointer(value); - } - return loadResult; - } + // Look through parens, __extension__, generic selection, etc. + e = e->IgnoreParens(); - // These casts can change the type, so remember that and - // soldier on. We only need to remember the outermost such - // cast, though. - case CK_CPointerToObjCPointerCast: - case CK_BlockPointerToObjCPointerCast: - case CK_AnyPointerToBlockPointerCast: - case CK_BitCast: - if (!resultType) - resultType = CGF.ConvertType(ce->getType()); - e = ce->getSubExpr(); - assert(e->getType()->hasPointerRepresentation()); - continue; - - // For consumptions, just emit the subexpression and thus elide - // the retain/release pair. - case CK_ARCConsumeObject: { - llvm::Value *result = CGF.EmitScalarExpr(ce->getSubExpr()); - if (resultType) result = CGF.Builder.CreateBitCast(result, resultType); - return TryEmitResult(result, true); - } + // Handle certain kinds of casts. + if (const CastExpr *ce = dyn_cast(e)) { + return asImpl().visitCastExpr(ce); - // Block extends are net +0. Naively, we could just recurse on - // the subexpression, but actually we need to ensure that the - // value is copied as a block, so there's a little filter here. - case CK_ARCExtendBlockObject: { - llvm::Value *result; // will be a +0 value + // Handle the comma operator. + } else if (auto op = dyn_cast(e)) { + return asImpl().visitBinaryOperator(op); - // If we can't safely assume the sub-expression will produce a - // block-copied value, emit the sub-expression at +0. - if (shouldEmitSeparateBlockRetain(ce->getSubExpr())) { - result = CGF.EmitScalarExpr(ce->getSubExpr()); + // TODO: handle conditional operators here - // Otherwise, try to emit the sub-expression at +1 recursively. - } else { - TryEmitResult subresult - = tryEmitARCRetainScalarExpr(CGF, ce->getSubExpr()); - result = subresult.getPointer(); - - // If that produced a retained value, just use that, - // possibly casting down. - if (subresult.getInt()) { - if (resultType) - result = CGF.Builder.CreateBitCast(result, resultType); - return TryEmitResult(result, true); - } + // For calls and message sends, use the retained-call logic. + // Delegate inits are a special case in that they're the only + // returns-retained expression that *isn't* surrounded by + // a consume. + } else if (isa(e) || + (isa(e) && + !cast(e)->isDelegateInitCall())) { + return asImpl().visitCall(e); - // Otherwise it's +0. - } + // Look through pseudo-object expressions. + } else if (const PseudoObjectExpr *pseudo = dyn_cast(e)) { + return asImpl().visitPseudoObjectExpr(pseudo); + } - // Retain the object as a block, then cast down. - result = CGF.EmitARCRetainBlock(result, /*mandatory*/ true); - if (resultType) result = CGF.Builder.CreateBitCast(result, resultType); - return TryEmitResult(result, true); - } + return asImpl().visitExpr(e); +} - // For reclaims, emit the subexpression as a retained call and - // skip the consumption. - case CK_ARCReclaimReturnedObject: { - llvm::Value *result = emitARCRetainCall(CGF, ce->getSubExpr()); - if (resultType) result = CGF.Builder.CreateBitCast(result, resultType); - return TryEmitResult(result, true); - } +namespace { - default: - break; - } +/// An emitter for +1 results. +struct ARCRetainExprEmitter : + public ARCExprEmitter { - // Skip __extension__. - } else if (const UnaryOperator *op = dyn_cast(e)) { - if (op->getOpcode() == UO_Extension) { - e = op->getSubExpr(); - continue; - } + ARCRetainExprEmitter(CodeGenFunction &CGF) : ARCExprEmitter(CGF) {} + + llvm::Value *getValueOfResult(TryEmitResult result) { + return result.getPointer(); + } + + TryEmitResult emitBitCast(TryEmitResult result, llvm::Type *resultType) { + llvm::Value *value = result.getPointer(); + value = CGF.Builder.CreateBitCast(value, resultType); + result.setPointer(value); + return result; + } + + TryEmitResult visitLValueToRValue(const Expr *e) { + return tryEmitARCRetainLoadOfScalar(CGF, e); + } + + /// For consumptions, just emit the subexpression and thus elide + /// the retain/release pair. + TryEmitResult visitConsumeObject(const Expr *e) { + llvm::Value *result = CGF.EmitScalarExpr(e); + return TryEmitResult(result, true); + } + + /// Block extends are net +0. Naively, we could just recurse on + /// the subexpression, but actually we need to ensure that the + /// value is copied as a block, so there's a little filter here. + TryEmitResult visitExtendBlockObject(const Expr *e) { + llvm::Value *result; // will be a +0 value - // For calls and message sends, use the retained-call logic. - // Delegate inits are a special case in that they're the only - // returns-retained expression that *isn't* surrounded by - // a consume. - } else if (isa(e) || - (isa(e) && - !cast(e)->isDelegateInitCall())) { - llvm::Value *result = emitARCRetainCall(CGF, e); - if (resultType) result = CGF.Builder.CreateBitCast(result, resultType); - return TryEmitResult(result, true); - - // Look through pseudo-object expressions. - } else if (const PseudoObjectExpr *pseudo = dyn_cast(e)) { - TryEmitResult result - = tryEmitARCRetainPseudoObject(CGF, pseudo); - if (resultType) { - llvm::Value *value = result.getPointer(); - value = CGF.Builder.CreateBitCast(value, resultType); - result.setPointer(value); + // If we can't safely assume the sub-expression will produce a + // block-copied value, emit the sub-expression at +0. + if (shouldEmitSeparateBlockRetain(e)) { + result = CGF.EmitScalarExpr(e); + + // Otherwise, try to emit the sub-expression at +1 recursively. + } else { + TryEmitResult subresult = asImpl().visit(e); + + // If that produced a retained value, just use that. + if (subresult.getInt()) { + return subresult; } - return result; + + // Otherwise it's +0. + result = subresult.getPointer(); } - // Conservatively halt the search at any other expression kind. - break; + // Retain the object as a block. + result = CGF.EmitARCRetainBlock(result, /*mandatory*/ true); + return TryEmitResult(result, true); + } + + /// For reclaims, emit the subexpression as a retained call and + /// skip the consumption. + TryEmitResult visitReclaimReturnedObject(const Expr *e) { + llvm::Value *result = emitARCRetainCallResult(CGF, e); + return TryEmitResult(result, true); + } + + /// When we have an undecorated call, retroactively do a claim. + TryEmitResult visitCall(const Expr *e) { + llvm::Value *result = emitARCRetainCallResult(CGF, e); + return TryEmitResult(result, true); + } + + // TODO: maybe special-case visitBinAssignWeak? + + TryEmitResult visitExpr(const Expr *e) { + // We didn't find an obvious production, so emit what we've got and + // tell the caller that we didn't manage to retain. + llvm::Value *result = CGF.EmitScalarExpr(e); + return TryEmitResult(result, false); } +}; +} - // We didn't find an obvious production, so emit what we've got and - // tell the caller that we didn't manage to retain. - llvm::Value *result = CGF.EmitScalarExpr(e); - if (resultType) result = CGF.Builder.CreateBitCast(result, resultType); - return TryEmitResult(result, false); +static TryEmitResult +tryEmitARCRetainScalarExpr(CodeGenFunction &CGF, const Expr *e) { + return ARCRetainExprEmitter(CGF).visit(e); } static llvm::Value *emitARCRetainLoadOfScalar(CodeGenFunction &CGF, @@ -2807,6 +2992,96 @@ llvm::Value *CodeGenFunction::EmitObjCThrowOperand(const Expr *expr) { return EmitScalarExpr(expr); } +namespace { + +/// An emitter for assigning into an __unsafe_unretained context. +struct ARCUnsafeUnretainedExprEmitter : + public ARCExprEmitter { + + ARCUnsafeUnretainedExprEmitter(CodeGenFunction &CGF) : ARCExprEmitter(CGF) {} + + llvm::Value *getValueOfResult(llvm::Value *value) { + return value; + } + + llvm::Value *emitBitCast(llvm::Value *value, llvm::Type *resultType) { + return CGF.Builder.CreateBitCast(value, resultType); + } + + llvm::Value *visitLValueToRValue(const Expr *e) { + return CGF.EmitScalarExpr(e); + } + + /// For consumptions, just emit the subexpression and perform the + /// consumption like normal. + llvm::Value *visitConsumeObject(const Expr *e) { + llvm::Value *value = CGF.EmitScalarExpr(e); + return CGF.EmitObjCConsumeObject(e->getType(), value); + } + + /// No special logic for block extensions. (This probably can't + /// actually happen in this emitter, though.) + llvm::Value *visitExtendBlockObject(const Expr *e) { + return CGF.EmitARCExtendBlockObject(e); + } + + /// For reclaims, perform an unsafeClaim if that's enabled. + llvm::Value *visitReclaimReturnedObject(const Expr *e) { + return CGF.EmitARCReclaimReturnedObject(e, /*unsafe*/ true); + } + + /// When we have an undecorated call, just emit it without adding + /// the unsafeClaim. + llvm::Value *visitCall(const Expr *e) { + return CGF.EmitScalarExpr(e); + } + + /// Just do normal scalar emission in the default case. + llvm::Value *visitExpr(const Expr *e) { + return CGF.EmitScalarExpr(e); + } +}; +} + +static llvm::Value *emitARCUnsafeUnretainedScalarExpr(CodeGenFunction &CGF, + const Expr *e) { + return ARCUnsafeUnretainedExprEmitter(CGF).visit(e); +} + +/// EmitARCUnsafeUnretainedScalarExpr - Semantically equivalent to +/// immediately releasing the resut of EmitARCRetainScalarExpr, but +/// avoiding any spurious retains, including by performing reclaims +/// with objc_unsafeClaimAutoreleasedReturnValue. +llvm::Value *CodeGenFunction::EmitARCUnsafeUnretainedScalarExpr(const Expr *e) { + // Look through full-expressions. + if (const ExprWithCleanups *cleanups = dyn_cast(e)) { + enterFullExpression(cleanups); + RunCleanupsScope scope(*this); + return emitARCUnsafeUnretainedScalarExpr(*this, cleanups->getSubExpr()); + } + + return emitARCUnsafeUnretainedScalarExpr(*this, e); +} + +std::pair +CodeGenFunction::EmitARCStoreUnsafeUnretained(const BinaryOperator *e, + bool ignored) { + // Evaluate the RHS first. If we're ignoring the result, assume + // that we can emit at an unsafe +0. + llvm::Value *value; + if (ignored) { + value = EmitARCUnsafeUnretainedScalarExpr(e->getRHS()); + } else { + value = EmitScalarExpr(e->getRHS()); + } + + // Emit the LHS and perform the store. + LValue lvalue = EmitLValue(e->getLHS()); + EmitStoreOfScalar(value, lvalue); + + return std::pair(std::move(lvalue), value); +} + std::pair CodeGenFunction::EmitARCStoreStrong(const BinaryOperator *e, bool ignored) { diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index 3dc0f35b0db..961d6aed0f6 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -2797,19 +2797,25 @@ class CodeGenFunction : public CodeGenTypeCache { llvm::Value *EmitARCAutoreleaseReturnValue(llvm::Value *value); llvm::Value *EmitARCRetainAutoreleaseReturnValue(llvm::Value *value); llvm::Value *EmitARCRetainAutoreleasedReturnValue(llvm::Value *value); + llvm::Value *EmitARCUnsafeClaimAutoreleasedReturnValue(llvm::Value *value); std::pair EmitARCStoreAutoreleasing(const BinaryOperator *e); std::pair EmitARCStoreStrong(const BinaryOperator *e, bool ignored); + std::pair + EmitARCStoreUnsafeUnretained(const BinaryOperator *e, bool ignored); llvm::Value *EmitObjCThrowOperand(const Expr *expr); llvm::Value *EmitObjCConsumeObject(QualType T, llvm::Value *Ptr); llvm::Value *EmitObjCExtendObjectLifetime(QualType T, llvm::Value *Ptr); llvm::Value *EmitARCExtendBlockObject(const Expr *expr); + llvm::Value *EmitARCReclaimReturnedObject(const Expr *e, + bool allowUnsafeClaim); llvm::Value *EmitARCRetainScalarExpr(const Expr *expr); llvm::Value *EmitARCRetainAutoreleaseScalarExpr(const Expr *expr); + llvm::Value *EmitARCUnsafeUnretainedScalarExpr(const Expr *expr); void EmitARCIntrinsicUse(ArrayRef values); diff --git a/lib/CodeGen/CodeGenModule.h b/lib/CodeGen/CodeGenModule.h index 046bc2a9fe5..efebcdac702 100644 --- a/lib/CodeGen/CodeGenModule.h +++ b/lib/CodeGen/CodeGenModule.h @@ -166,6 +166,9 @@ struct ObjCEntrypoints { /// id objc_storeWeak(id*, id); llvm::Constant *objc_storeWeak; + /// id objc_unsafeClaimAutoreleasedReturnValue(id); + llvm::Constant *objc_unsafeClaimAutoreleasedReturnValue; + /// A void(void) inline asm to use to mark that the return value of /// a call will be immediately retain. llvm::InlineAsm *retainAutoreleasedReturnValueMarker; diff --git a/test/CodeGenObjC/arc-unsafeclaim.m b/test/CodeGenObjC/arc-unsafeclaim.m new file mode 100644 index 00000000000..cda00b0a2f5 --- /dev/null +++ b/test/CodeGenObjC/arc-unsafeclaim.m @@ -0,0 +1,231 @@ +// Make sure it works on x86-64. +// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fobjc-runtime=macosx-10.11 -fobjc-arc -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK -check-prefix=CHECK-UNOPTIMIZED + +// Make sure it works on ARM. +// RUN: %clang_cc1 -triple arm64-apple-ios9 -fobjc-runtime=ios-9.0 -fobjc-arc -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK -check-prefix=CHECK-UNOPTIMIZED -check-prefix=CHECK-MARKED +// RUN: %clang_cc1 -triple arm64-apple-ios9 -fobjc-runtime=ios-9.0 -fobjc-arc -O -disable-llvm-optzns -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK -check-prefix=CHECK-OPTIMIZED + +// Make sure it works on ARM64. +// RUN: %clang_cc1 -triple armv7-apple-ios9 -fobjc-runtime=ios-9.0 -fobjc-arc -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK -check-prefix=CHECK-UNOPTIMIZED -check-prefix=CHECK-MARKED +// RUN: %clang_cc1 -triple armv7-apple-ios9 -fobjc-runtime=ios-9.0 -fobjc-arc -O -disable-llvm-optzns -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK -check-prefix=CHECK-OPTIMIZED + +// Make sure that it's implicitly disabled if the runtime version isn't high enough. +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-runtime=macosx-10.10 -fobjc-arc -emit-llvm -o - %s | FileCheck %s -check-prefix=DISABLED +// RUN: %clang_cc1 -triple arm64-apple-ios8 -fobjc-runtime=ios-8 -fobjc-arc -emit-llvm -o - %s | FileCheck %s -check-prefix=DISABLED -check-prefix=DISABLED-MARKED + +@class A; + +A *makeA(void); + +void test_assign() { + __unsafe_unretained id x; + x = makeA(); +} +// CHECK-LABEL: define void @test_assign() +// CHECK: [[X:%.*]] = alloca i8* +// CHECK: [[T0:%.*]] = call [[A:.*]]* @makeA() +// CHECK-MARKED-NEXT: call void asm sideeffect +// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8* +// CHECK-NEXT: [[T2:%.*]] = call i8* @objc_unsafeClaimAutoreleasedReturnValue(i8* [[T1]]) +// CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to [[A]]* +// CHECK-NEXT: [[T4:%.*]] = bitcast [[A]]* [[T3]] to i8* +// CHECK-NEXT: store i8* [[T4]], i8** [[X]] +// CHECK-OPTIMIZED-NEXT: bitcast +// CHECK-OPTIMIZED-NEXT: lifetime.end +// CHECK-NEXT: ret void + +// DISABLED-LABEL: define void @test_assign() +// DISABLED: [[T0:%.*]] = call [[A:.*]]* @makeA() +// DISABLED-MARKED-NEXT: call void asm sideeffect +// DISABLED-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8* +// DISABLED-NEXT: [[T2:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T1]]) + +void test_assign_assign() { + __unsafe_unretained id x, y; + x = y = makeA(); +} +// CHECK-LABEL: define void @test_assign_assign() +// CHECK: [[X:%.*]] = alloca i8* +// CHECK: [[Y:%.*]] = alloca i8* +// CHECK: [[T0:%.*]] = call [[A]]* @makeA() +// CHECK-MARKED-NEXT: call void asm sideeffect +// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8* +// CHECK-NEXT: [[T2:%.*]] = call i8* @objc_unsafeClaimAutoreleasedReturnValue(i8* [[T1]]) +// CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to [[A]]* +// CHECK-NEXT: [[T4:%.*]] = bitcast [[A]]* [[T3]] to i8* +// CHECK-NEXT: store i8* [[T4]], i8** [[Y]] +// CHECK-NEXT: store i8* [[T4]], i8** [[X]] +// CHECK-OPTIMIZED-NEXT: bitcast +// CHECK-OPTIMIZED-NEXT: lifetime.end +// CHECK-OPTIMIZED-NEXT: bitcast +// CHECK-OPTIMIZED-NEXT: lifetime.end +// CHECK-NEXT: ret void + +void test_strong_assign_assign() { + __strong id x; + __unsafe_unretained id y; + x = y = makeA(); +} +// CHECK-LABEL: define void @test_strong_assign_assign() +// CHECK: [[X:%.*]] = alloca i8* +// CHECK: [[Y:%.*]] = alloca i8* +// CHECK: [[T0:%.*]] = call [[A]]* @makeA() +// CHECK-MARKED-NEXT: call void asm sideeffect +// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8* +// CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T1]]) +// CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to [[A]]* +// CHECK-NEXT: [[T4:%.*]] = bitcast [[A]]* [[T3]] to i8* +// CHECK-NEXT: store i8* [[T4]], i8** [[Y]] +// CHECK-NEXT: [[OLD:%.*]] = load i8*, i8** [[X]] +// CHECK-NEXT: store i8* [[T4]], i8** [[X]] +// CHECK-NEXT: call void @objc_release(i8* [[OLD]] +// CHECK-OPTIMIZED-NEXT: bitcast +// CHECK-OPTIMIZED-NEXT: lifetime.end +// CHECK-UNOPTIMIZED-NEXT: call void @objc_storeStrong(i8** [[X]], i8* null) +// CHECK-OPTIMIZED-NEXT: [[T0:%.*]] = load i8*, i8** [[X]] +// CHECK-OPTIMIZED-NEXT: call void @objc_release(i8* [[T0]]) +// CHECK-OPTIMIZED-NEXT: bitcast +// CHECK-OPTIMIZED-NEXT: lifetime.end +// CHECK-NEXT: ret void + +void test_assign_strong_assign() { + __unsafe_unretained id x; + __strong id y; + x = y = makeA(); +} +// CHECK-LABEL: define void @test_assign_strong_assign() +// CHECK: [[X:%.*]] = alloca i8* +// CHECK: [[Y:%.*]] = alloca i8* +// CHECK: [[T0:%.*]] = call [[A]]* @makeA() +// CHECK-MARKED-NEXT: call void asm sideeffect +// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8* +// CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T1]]) +// CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to [[A]]* +// CHECK-NEXT: [[T4:%.*]] = bitcast [[A]]* [[T3]] to i8* +// CHECK-NEXT: [[OLD:%.*]] = load i8*, i8** [[Y]] +// CHECK-NEXT: store i8* [[T4]], i8** [[Y]] +// CHECK-NEXT: call void @objc_release(i8* [[OLD]] +// CHECK-NEXT: store i8* [[T4]], i8** [[X]] +// CHECK-UNOPTIMIZED-NEXT: call void @objc_storeStrong(i8** [[Y]], i8* null) +// CHECK-OPTIMIZED-NEXT: [[T0:%.*]] = load i8*, i8** [[Y]] +// CHECK-OPTIMIZED-NEXT: call void @objc_release(i8* [[T0]]) +// CHECK-OPTIMIZED-NEXT: bitcast +// CHECK-OPTIMIZED-NEXT: lifetime.end +// CHECK-OPTIMIZED-NEXT: bitcast +// CHECK-OPTIMIZED-NEXT: lifetime.end +// CHECK-NEXT: ret void + +void test_init() { + __unsafe_unretained id x = makeA(); +} +// CHECK-LABEL: define void @test_init() +// CHECK: [[X:%.*]] = alloca i8* +// CHECK: [[T0:%.*]] = call [[A]]* @makeA() +// CHECK-MARKED-NEXT: call void asm sideeffect +// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8* +// CHECK-NEXT: [[T2:%.*]] = call i8* @objc_unsafeClaimAutoreleasedReturnValue(i8* [[T1]]) +// CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to [[A]]* +// CHECK-NEXT: [[T4:%.*]] = bitcast [[A]]* [[T3]] to i8* +// CHECK-NEXT: store i8* [[T4]], i8** [[X]] +// CHECK-OPTIMIZED-NEXT: bitcast +// CHECK-OPTIMIZED-NEXT: lifetime.end +// CHECK-NEXT: ret void + +void test_init_assignment() { + __unsafe_unretained id x; + __unsafe_unretained id y = x = makeA(); +} +// CHECK-LABEL: define void @test_init_assignment() +// CHECK: [[X:%.*]] = alloca i8* +// CHECK: [[Y:%.*]] = alloca i8* +// CHECK: [[T0:%.*]] = call [[A]]* @makeA() +// CHECK-MARKED-NEXT: call void asm sideeffect +// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8* +// CHECK-NEXT: [[T2:%.*]] = call i8* @objc_unsafeClaimAutoreleasedReturnValue(i8* [[T1]]) +// CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to [[A]]* +// CHECK-NEXT: [[T4:%.*]] = bitcast [[A]]* [[T3]] to i8* +// CHECK-NEXT: store i8* [[T4]], i8** [[X]] +// CHECK-NEXT: store i8* [[T4]], i8** [[Y]] +// CHECK-OPTIMIZED-NEXT: bitcast +// CHECK-OPTIMIZED-NEXT: lifetime.end +// CHECK-OPTIMIZED-NEXT: bitcast +// CHECK-OPTIMIZED-NEXT: lifetime.end +// CHECK-NEXT: ret void + +void test_strong_init_assignment() { + __unsafe_unretained id x; + __strong id y = x = makeA(); +} +// CHECK-LABEL: define void @test_strong_init_assignment() +// CHECK: [[X:%.*]] = alloca i8* +// CHECK: [[Y:%.*]] = alloca i8* +// CHECK: [[T0:%.*]] = call [[A]]* @makeA() +// CHECK-MARKED-NEXT: call void asm sideeffect +// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8* +// CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T1]]) +// CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to [[A]]* +// CHECK-NEXT: [[T4:%.*]] = bitcast [[A]]* [[T3]] to i8* +// CHECK-NEXT: store i8* [[T4]], i8** [[X]] +// CHECK-NEXT: store i8* [[T4]], i8** [[Y]] +// CHECK-UNOPTIMIZED-NEXT: call void @objc_storeStrong(i8** [[Y]], i8* null) +// CHECK-OPTIMIZED-NEXT: [[T0:%.*]] = load i8*, i8** [[Y]] +// CHECK-OPTIMIZED-NEXT: call void @objc_release(i8* [[T0]]) +// CHECK-OPTIMIZED-NEXT: bitcast +// CHECK-OPTIMIZED-NEXT: lifetime.end +// CHECK-OPTIMIZED-NEXT: bitcast +// CHECK-OPTIMIZED-NEXT: lifetime.end +// CHECK-NEXT: ret void + +void test_init_strong_assignment() { + __strong id x; + __unsafe_unretained id y = x = makeA(); +} +// CHECK-LABEL: define void @test_init_strong_assignment() +// CHECK: [[X:%.*]] = alloca i8* +// CHECK: [[Y:%.*]] = alloca i8* +// CHECK: [[T0:%.*]] = call [[A]]* @makeA() +// CHECK-MARKED-NEXT: call void asm sideeffect +// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8* +// CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T1]]) +// CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to [[A]]* +// CHECK-NEXT: [[T4:%.*]] = bitcast [[A]]* [[T3]] to i8* +// CHECK-NEXT: [[OLD:%.*]] = load i8*, i8** [[X]] +// CHECK-NEXT: store i8* [[T4]], i8** [[X]] +// CHECK-NEXT: call void @objc_release(i8* [[OLD]]) +// CHECK-NEXT: store i8* [[T4]], i8** [[Y]] +// CHECK-OPTIMIZED-NEXT: bitcast +// CHECK-OPTIMIZED-NEXT: lifetime.end +// CHECK-UNOPTIMIZED-NEXT: call void @objc_storeStrong(i8** [[X]], i8* null) +// CHECK-OPTIMIZED-NEXT: [[T0:%.*]] = load i8*, i8** [[X]] +// CHECK-OPTIMIZED-NEXT: call void @objc_release(i8* [[T0]]) +// CHECK-OPTIMIZED-NEXT: bitcast +// CHECK-OPTIMIZED-NEXT: lifetime.end +// CHECK-NEXT: ret void + +void test_ignored() { + makeA(); +} +// CHECK-LABEL: define void @test_ignored() +// CHECK: [[T0:%.*]] = call [[A]]* @makeA() +// CHECK-MARKED-NEXT: call void asm sideeffect +// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8* +// CHECK-NEXT: [[T2:%.*]] = call i8* @objc_unsafeClaimAutoreleasedReturnValue(i8* [[T1]]) +// CHECK-NEXT: bitcast i8* [[T2]] to [[A]]* +// CHECK-NEXT: ret void + +void test_cast_to_void() { + (void) makeA(); +} +// CHECK-LABEL: define void @test_cast_to_void() +// CHECK: [[T0:%.*]] = call [[A]]* @makeA() +// CHECK-MARKED-NEXT: call void asm sideeffect +// CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8* +// CHECK-NEXT: [[T2:%.*]] = call i8* @objc_unsafeClaimAutoreleasedReturnValue(i8* [[T1]]) +// CHECK-NEXT: bitcast i8* [[T2]] to [[A]]* +// CHECK-NEXT: ret void + + + +// This is always at the end of the module. + +// CHECK-OPTIMIZED: !clang.arc.retainAutoreleasedReturnValueMarker = !{!0} From 51ad5eedf992d9bb133117cec8daf9c5311a0251 Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 5 Feb 2016 21:37:38 +0000 Subject: [PATCH 082/742] Add an ARC autoreleased-return-value caller marker on i386. rdar://24531556 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259932 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/TargetInfo.cpp | 5 +++++ test/CodeGenObjC/arc-i386.m | 43 +++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 test/CodeGenObjC/arc-i386.m diff --git a/lib/CodeGen/TargetInfo.cpp b/lib/CodeGen/TargetInfo.cpp index d7816260118..cdb0a6a168b 100644 --- a/lib/CodeGen/TargetInfo.cpp +++ b/lib/CodeGen/TargetInfo.cpp @@ -920,6 +920,11 @@ class X86_32TargetCodeGenInfo : public TargetCodeGenInfo { ('T' << 24); return llvm::ConstantInt::get(CGM.Int32Ty, Sig); } + + StringRef getARCRetainAutoreleasedReturnValueMarker() const override { + return "movl\t%ebp, %ebp" + "\t\t## marker for objc_retainAutoreleaseReturnValue"; + } }; } diff --git a/test/CodeGenObjC/arc-i386.m b/test/CodeGenObjC/arc-i386.m new file mode 100644 index 00000000000..7693a8f2b6f --- /dev/null +++ b/test/CodeGenObjC/arc-i386.m @@ -0,0 +1,43 @@ +// RUN: %clang_cc1 -triple i386-apple-darwin10 -emit-llvm -fblocks -fobjc-arc -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple i386-apple-iossimulator6.0 -emit-llvm -fblocks -fobjc-arc -o - %s | FileCheck %s + +// : implement objc_retainAutoreleasedReturnValue on i386 + +// CHECK-LABEL: define i8* @test0() +id test0(void) { + extern id test0_helper(void); + // CHECK: [[T0:%.*]] = call i8* @test0_helper() + // CHECK-NEXT: ret i8* [[T0]] + return test0_helper(); +} + +// CHECK-LABEL: define void @test1() +void test1(void) { + extern id test1_helper(void); + // CHECK: [[T0:%.*]] = call i8* @test1_helper() + // CHECK-NEXT: call void asm sideeffect "mov + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]]) + // CHECK-NEXT: store i8* [[T1]], + // CHECK-NEXT: call void @objc_storeStrong( + // CHECK-NEXT: ret void + id x = test1_helper(); +} + +// rdar://problem/12133032 +// CHECK-LABEL: define {{.*}} @test2() +@class A; +A *test2(void) { + extern A *test2_helper(void); + // CHECK: [[T0:%.*]] = call [[A:%.*]]* @test2_helper() + // CHECK-NEXT: ret [[A]]* [[T0]] + return test2_helper(); +} + +// CHECK-LABEL: define i8* @test3() +id test3(void) { + extern A *test3_helper(void); + // CHECK: [[T0:%.*]] = call [[A]]* @test3_helper() + // CHECK-NEXT: [[T1:%.*]] = bitcast [[A]]* [[T0]] to i8* + // CHECK-NEXT: ret i8* [[T1]] + return test3_helper(); +} From 2cabb1c27fa5df602956643a601f2f4059a80c82 Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes Date: Fri, 5 Feb 2016 19:36:39 +0000 Subject: [PATCH 083/742] [Parser] Perform CachedTokens update dependent on token consumption In the context where we break one tok::greatergreater into two tok::greater in order to correctly update the cached tokens; update the CachedTokens with two tok::greater only if ParseGreaterThanInTemplateList clients asks to consume the last token. Otherwise we only need to add one because the second is already added later on, as a not yet cached token. Differential Revision: http://reviews.llvm.org/D16906 rdar://problem/24488367 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259910 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 83c02698258871896f3f173531ad6c8660ea1377) --- lib/Parse/ParseTemplate.cpp | 8 ++++++-- test/Parser/objcxx14-protocol-in-template.mm | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 test/Parser/objcxx14-protocol-in-template.mm diff --git a/lib/Parse/ParseTemplate.cpp b/lib/Parse/ParseTemplate.cpp index 4e372e864b2..ca50315bdcd 100644 --- a/lib/Parse/ParseTemplate.cpp +++ b/lib/Parse/ParseTemplate.cpp @@ -851,8 +851,12 @@ bool Parser::ParseGreaterThanInTemplateList(SourceLocation &RAngleLoc, RemainingToken == tok::greater && PP.IsPreviousCachedToken(PrevTok)) { PrevTok.setKind(RemainingToken); PrevTok.setLength(1); - Token NewToks[] = {PrevTok, Tok}; - PP.ReplacePreviousCachedToken(NewToks); + // Break tok::greatergreater into two tok::greater but only add the second + // one in case the client asks to consume the last token. + if (ConsumeLastToken) + PP.ReplacePreviousCachedToken({PrevTok, Tok}); + else + PP.ReplacePreviousCachedToken({PrevTok}); } if (!ConsumeLastToken) { diff --git a/test/Parser/objcxx14-protocol-in-template.mm b/test/Parser/objcxx14-protocol-in-template.mm new file mode 100644 index 00000000000..36da92e251f --- /dev/null +++ b/test/Parser/objcxx14-protocol-in-template.mm @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++14 %s + +template class vector {}; +@protocol P @end + +// expected-no-diagnostics + +template void F(Functor functor) {} + +// Test protocol in template within lambda capture initializer context. +void z() { + id

x = 0; + (void)x; + F( [ x = vector>{} ] {} ); +} From 6f5086efb714ac2d6787c4f71a80de17e4bbe743 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 3 Feb 2016 19:13:08 +0000 Subject: [PATCH 084/742] [Sema debugger support] Require non-void types to be complete in unknown-anytype casts. When performing a cast from an __unknown_anytype function call to a non-void type, we need to make sure that type is complete. Fixes rdar://problem/23959960. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259681 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/SemaExpr.cpp | 6 ++++++ test/SemaCXX/unknown-anytype.cpp | 11 +++++++++++ 2 files changed, 17 insertions(+) diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index c6ebce8c1af..d8dd0ba119b 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -14470,6 +14470,12 @@ ExprResult RebuildUnknownAnyExpr::resolveDecl(Expr *E, ValueDecl *VD) { ExprResult Sema::checkUnknownAnyCast(SourceRange TypeRange, QualType CastType, Expr *CastExpr, CastKind &CastKind, ExprValueKind &VK, CXXCastPath &Path) { + // The type we're casting to must be either void or complete. + if (!CastType->isVoidType() && + RequireCompleteType(TypeRange.getBegin(), CastType, + diag::err_typecheck_cast_to_incomplete)) + return ExprError(); + // Rewrite the casted expression from scratch. ExprResult result = RebuildUnknownAnyExpr(*this, CastType).Visit(CastExpr); if (!result.isUsable()) return ExprError(); diff --git a/test/SemaCXX/unknown-anytype.cpp b/test/SemaCXX/unknown-anytype.cpp index a07ec83d10a..95ad0409340 100644 --- a/test/SemaCXX/unknown-anytype.cpp +++ b/test/SemaCXX/unknown-anytype.cpp @@ -45,3 +45,14 @@ namespace test4 { int x = (int) test1; // expected-error {{function 'test1' with unknown type must be given a function type}} } } + +// rdar://problem/23959960 +namespace test5 { + template struct X; // expected-note{{template is declared here}} + + extern __unknown_anytype test0(...); + + void test() { + (X)test0(); // expected-error{{implicit instantiation of undefined template 'test5::X'}} + } +} From 1dccc39fbe98c4175cff06ada7abd289d89857f9 Mon Sep 17 00:00:00 2001 From: Artem Dergachev Date: Mon, 1 Feb 2016 09:29:17 +0000 Subject: [PATCH 085/742] [analyzer] Use a wider integer type for an array index. Avoids unexpected overflows while performing pointer arithmetics in 64-bit code. Moreover, neither PointerDiffType nor 'int' can be used as a common array index type because arrays may have size (and indexes) more than PTRDIFF_MAX but less than SIZE_MAX. Patch by Aleksei Sidorin! Differential Revision: http://reviews.llvm.org/D16063 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259345 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 390547e298db5aad606d908a88cfde6880a747b2) --- .../Core/PathSensitive/SValBuilder.h | 2 +- test/Analysis/index-type.c | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 test/Analysis/index-type.c diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h index 3c47114e2de..1f578bd71d5 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h @@ -65,7 +65,7 @@ class SValBuilder { SymMgr(context, BasicVals, alloc), MemMgr(context, alloc), StateMgr(stateMgr), - ArrayIndexTy(context.IntTy), + ArrayIndexTy(context.LongLongTy), ArrayIndexWidth(context.getTypeSize(ArrayIndexTy)) {} virtual ~SValBuilder() {} diff --git a/test/Analysis/index-type.c b/test/Analysis/index-type.c new file mode 100644 index 00000000000..fc638dfe741 --- /dev/null +++ b/test/Analysis/index-type.c @@ -0,0 +1,39 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core,alpha.security.ArrayBoundV2 -verify %s +// RUN: %clang_cc1 -triple i386-apple-darwin10 -analyze -analyzer-checker=core,alpha.security.ArrayBoundV2 -DM32 -verify %s +// expected-no-diagnostics + +#define UINT_MAX (~0u) + +#ifdef M32 + +#define X86_ARRAY_SIZE (UINT_MAX/2 + 4) + +void testIndexTooBig() { + char arr[X86_ARRAY_SIZE]; + char *ptr = arr + UINT_MAX/2; + ptr += 2; // index shouldn't overflow + *ptr = 42; // no-warning +} + +#else // 64-bit tests + +#define ARRAY_SIZE 0x100000000 + +void testIndexOverflow64() { + char arr[ARRAY_SIZE]; + char *ptr = arr + UINT_MAX/2; + ptr += 2; // don't overflow 64-bit index + *ptr = 42; // no-warning +} + +#define ULONG_MAX (~0ul) +#define BIG_INDEX (ULONG_MAX/16) + +void testIndexTooBig64() { + char arr[ULONG_MAX/8-1]; + char *ptr = arr + BIG_INDEX; + ptr += 2; // don't overflow 64-bit index + *ptr = 42; // no-warning +} + +#endif From 059e74a5b4303de060159be8fba71e13ebf26177 Mon Sep 17 00:00:00 2001 From: Benjamin Kramer Date: Tue, 2 Feb 2016 14:24:11 +0000 Subject: [PATCH 086/742] [StaticAnalyzer] Pull SymExpr and SymbolData into its own header to avoid cyclic includes. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259506 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit e86b6f584b209b34c85ecbb4afe72b62a59a5c44) --- .../Core/BugReporter/BugReporterVisitor.h | 1 + .../Core/PathSensitive/Environment.h | 1 + .../Core/PathSensitive/MemRegion.h | 1 + .../Core/PathSensitive/SValBuilder.h | 1 + .../StaticAnalyzer/Core/PathSensitive/SVals.h | 4 +- .../Core/PathSensitive/SymExpr.h | 109 ++++++++++++++++++ .../Core/PathSensitive/SymbolManager.h | 84 +------------- 7 files changed, 118 insertions(+), 83 deletions(-) create mode 100644 include/clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h index 197d27a2f37..c954bbfad8c 100644 --- a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h +++ b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h @@ -19,6 +19,7 @@ #include "llvm/ADT/FoldingSet.h" namespace clang { +class CFGBlock; namespace ento { diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/Environment.h b/include/clang/StaticAnalyzer/Core/PathSensitive/Environment.h index cc3779d743f..a66e1a1aed0 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/Environment.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/Environment.h @@ -26,6 +26,7 @@ namespace ento { class EnvironmentManager; class SValBuilder; +class SymbolReaper; /// An entry in the environment consists of a Stmt and an LocationContext. /// This allows the environment to manage context-sensitive bindings, diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h b/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h index 46b01010d0d..41935aef0a3 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h @@ -21,6 +21,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ExprObjC.h" +#include "clang/Analysis/AnalysisContext.h" #include "clang/Basic/LLVM.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "llvm/ADT/FoldingSet.h" diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h index 1f578bd71d5..e4be349c026 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h @@ -21,6 +21,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" namespace clang { diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h index 8b353bcbdfa..dbc7c99c610 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SVals.h @@ -15,9 +15,11 @@ #ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SVALS_H #define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SVALS_H +#include "clang/AST/Expr.h" #include "clang/Basic/LLVM.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableList.h" //==------------------------------------------------------------------------==// diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h new file mode 100644 index 00000000000..be9646ba6a6 --- /dev/null +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h @@ -0,0 +1,109 @@ +//== SymExpr.h - Management of Symbolic Values ------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines SymExpr and SymbolData. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SYMEXPR_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SYMEXPR_H + +#include "clang/AST/Type.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/raw_ostream.h" + +namespace clang { +namespace ento { + +/// \brief Symbolic value. These values used to capture symbolic execution of +/// the program. +class SymExpr : public llvm::FoldingSetNode { + virtual void anchor(); + +public: + enum Kind { +#define SYMBOL(Id, Parent) Id##Kind, +#define SYMBOL_RANGE(Id, First, Last) BEGIN_##Id = First, END_##Id = Last, +#include "clang/StaticAnalyzer/Core/PathSensitive/Symbols.def" + }; + +private: + Kind K; + +protected: + SymExpr(Kind k) : K(k) {} + +public: + virtual ~SymExpr() {} + + Kind getKind() const { return K; } + + virtual void dump() const; + + virtual void dumpToStream(raw_ostream &os) const {} + + virtual QualType getType() const = 0; + virtual void Profile(llvm::FoldingSetNodeID &profile) = 0; + + /// \brief Iterator over symbols that the current symbol depends on. + /// + /// For SymbolData, it's the symbol itself; for expressions, it's the + /// expression symbol and all the operands in it. Note, SymbolDerived is + /// treated as SymbolData - the iterator will NOT visit the parent region. + class symbol_iterator { + SmallVector itr; + void expand(); + + public: + symbol_iterator() {} + symbol_iterator(const SymExpr *SE); + + symbol_iterator &operator++(); + const SymExpr *operator*(); + + bool operator==(const symbol_iterator &X) const; + bool operator!=(const symbol_iterator &X) const; + }; + + symbol_iterator symbol_begin() const { return symbol_iterator(this); } + static symbol_iterator symbol_end() { return symbol_iterator(); } + + unsigned computeComplexity() const; +}; + +typedef const SymExpr *SymbolRef; +typedef SmallVector SymbolRefSmallVectorTy; + +typedef unsigned SymbolID; +/// \brief A symbol representing data which can be stored in a memory location +/// (region). +class SymbolData : public SymExpr { + void anchor() override; + const SymbolID Sym; + +protected: + SymbolData(Kind k, SymbolID sym) : SymExpr(k), Sym(sym) {} + +public: + ~SymbolData() override {} + + SymbolID getSymbolID() const { return Sym; } + + // Implement isa support. + static inline bool classof(const SymExpr *SE) { + Kind k = SE->getKind(); + return k >= BEGIN_SYMBOLS && k <= END_SYMBOLS; + } +}; + +} // namespace ento +} // namespace clang + +#endif diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h index dd35d65c0d2..30481ea3913 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h @@ -19,7 +19,9 @@ #include "clang/AST/Expr.h" #include "clang/Analysis/AnalysisContext.h" #include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/FoldingSet.h" @@ -32,92 +34,10 @@ namespace clang { namespace ento { class BasicValueFactory; - class MemRegion; class SubRegion; class TypedValueRegion; class VarRegion; -/// \brief Symbolic value. These values used to capture symbolic execution of -/// the program. -class SymExpr : public llvm::FoldingSetNode { - virtual void anchor(); -public: - enum Kind { -#define SYMBOL(Id, Parent) Id ## Kind, -#define SYMBOL_RANGE(Id, First, Last) BEGIN_##Id = First, END_##Id = Last, -#include "clang/StaticAnalyzer/Core/PathSensitive/Symbols.def" - }; - -private: - Kind K; - -protected: - SymExpr(Kind k) : K(k) {} - -public: - virtual ~SymExpr() {} - - Kind getKind() const { return K; } - - virtual void dump() const; - - virtual void dumpToStream(raw_ostream &os) const {} - - virtual QualType getType() const = 0; - virtual void Profile(llvm::FoldingSetNodeID& profile) = 0; - - /// \brief Iterator over symbols that the current symbol depends on. - /// - /// For SymbolData, it's the symbol itself; for expressions, it's the - /// expression symbol and all the operands in it. Note, SymbolDerived is - /// treated as SymbolData - the iterator will NOT visit the parent region. - class symbol_iterator { - SmallVector itr; - void expand(); - public: - symbol_iterator() {} - symbol_iterator(const SymExpr *SE); - - symbol_iterator &operator++(); - const SymExpr* operator*(); - - bool operator==(const symbol_iterator &X) const; - bool operator!=(const symbol_iterator &X) const; - }; - - symbol_iterator symbol_begin() const { - return symbol_iterator(this); - } - static symbol_iterator symbol_end() { return symbol_iterator(); } - - unsigned computeComplexity() const; -}; - -typedef const SymExpr* SymbolRef; -typedef SmallVector SymbolRefSmallVectorTy; - -typedef unsigned SymbolID; -/// \brief A symbol representing data which can be stored in a memory location -/// (region). -class SymbolData : public SymExpr { - void anchor() override; - const SymbolID Sym; - -protected: - SymbolData(Kind k, SymbolID sym) : SymExpr(k), Sym(sym) {} - -public: - ~SymbolData() override {} - - SymbolID getSymbolID() const { return Sym; } - - // Implement isa support. - static inline bool classof(const SymExpr *SE) { - Kind k = SE->getKind(); - return k >= BEGIN_SYMBOLS && k <= END_SYMBOLS; - } -}; - ///\brief A symbol representing the value stored at a MemRegion. class SymbolRegionValue : public SymbolData { const TypedValueRegion *R; From 6cc0c180aa0e80fc9cd74e7ec1daf0441de33392 Mon Sep 17 00:00:00 2001 From: Yury Gribov Date: Wed, 3 Feb 2016 13:35:33 +0000 Subject: [PATCH 087/742] [analyzer] AnalysisConsumer: print fully-qualified function name while displaying progress -analyzer-display progress option prints only function names which may be ambiguous. This patch forces AnalysisConsumer to print fully-qualified function names. Patch by Alex Sidorin! Differential Revision: http://reviews.llvm.org/D16804 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259646 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit b2f9c2bd5e520f31ac9c2541debf1709bb6b4bab) --- .../Frontend/AnalysisConsumer.cpp | 2 +- test/Analysis/analyze_display_progress.cpp | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 test/Analysis/analyze_display_progress.cpp diff --git a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp index d1446855e01..083ee51a995 100644 --- a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -274,7 +274,7 @@ class AnalysisConsumer : public AnalysisASTConsumer, llvm::errs() << ": " << Loc.getFilename(); if (isa(D) || isa(D)) { const NamedDecl *ND = cast(D); - llvm::errs() << ' ' << *ND << '\n'; + llvm::errs() << ' ' << ND->getQualifiedNameAsString() << '\n'; } else if (isa(D)) { llvm::errs() << ' ' << "block(line:" << Loc.getLine() << ",col:" diff --git a/test/Analysis/analyze_display_progress.cpp b/test/Analysis/analyze_display_progress.cpp new file mode 100644 index 00000000000..c84ab63de48 --- /dev/null +++ b/test/Analysis/analyze_display_progress.cpp @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -analyze -analyzer-display-progress %s 2>&1 | FileCheck %s + +void f() {}; +void g() {}; +void h() {} + +struct SomeStruct { + void f() {} +}; + +struct SomeOtherStruct { + void f() {} +}; + +namespace ns { + struct SomeStruct { + void f() {} + }; +} + +// CHECK: analyze_display_progress.cpp f +// CHECK: analyze_display_progress.cpp g +// CHECK: analyze_display_progress.cpp h +// CHECK: analyze_display_progress.cpp SomeStruct::f +// CHECK: analyze_display_progress.cpp SomeOtherStruct::f +// CHECK: analyze_display_progress.cpp ns::SomeStruct::f From 7174161cc15a40a15e4a5db7b633f4a65bcfaa76 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Fri, 5 Feb 2016 04:22:15 +0000 Subject: [PATCH 088/742] [analyzer] Suppress localization diagnostics in debug classes and methods. If the class or method name case-insensitively contains the term "debug", suppress warnings about string constants flowing to user-facing UI APIs. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259875 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 4acb0a7d13ebc582f377efcb4c2dc020b6135fc1) --- .../Checkers/LocalizationChecker.cpp | 35 +++++++++++++++++++ test/Analysis/localization.m | 19 ++++++++++ 2 files changed, 54 insertions(+) diff --git a/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp b/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp index 56346cd4f70..504b8b30187 100644 --- a/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp @@ -619,11 +619,46 @@ void NonLocalizedStringChecker::setNonLocalizedState(const SVal S, } } + +static bool isDebuggingName(std::string name) { + return StringRef(name).lower().find("debug") != StringRef::npos; +} + +/// Returns true when, heuristically, the analyzer may be analyzing debugging +/// code. We use this to suppress localization diagnostics in un-localized user +/// interfaces that are only used for debugging and are therefore not user +/// facing. +static bool isDebuggingContext(CheckerContext &C) { + const Decl *D = C.getCurrentAnalysisDeclContext()->getDecl(); + if (!D) + return false; + + if (auto *ND = dyn_cast(D)) { + if (isDebuggingName(ND->getNameAsString())) + return true; + } + + const DeclContext *DC = D->getDeclContext(); + + if (auto *CD = dyn_cast(DC)) { + if (isDebuggingName(CD->getNameAsString())) + return true; + } + + return false; +} + + /// Reports a localization error for the passed in method call and SVal void NonLocalizedStringChecker::reportLocalizationError( SVal S, const ObjCMethodCall &M, CheckerContext &C, int argumentNumber) const { + // Don't warn about localization errors in classes and methods that + // may be debug code. + if (isDebuggingContext(C)) + return; + ExplodedNode *ErrNode = C.getPredecessor(); static CheckerProgramPointTag Tag("NonLocalizedStringChecker", "UnlocalizedString"); diff --git a/test/Analysis/localization.m b/test/Analysis/localization.m index ce55609b1bf..cf0697c76f5 100644 --- a/test/Analysis/localization.m +++ b/test/Analysis/localization.m @@ -90,6 +90,13 @@ - (void)testOneCharacterUTFStringsDoNotGiveAWarning { [testLabel setText:bar]; // no-warning } + +// Suppress diagnostic about user-facing string constants when the method name +// contains the term "Debug". +- (void)debugScreen:(UILabel *)label { + label.text = @"Unlocalized"; +} + // Plural Misuse Checker Tests // These tests are modeled off incorrect uses of the many-one pattern // from real projects. @@ -205,3 +212,15 @@ - (NSString *)test6:(int)sectionIndex { // } @end + + +// Suppress diagnostic about user-facing string constants when the class name +// contains "Debug" +@interface MyDebugView : NSObject +@end + +@implementation MyDebugView +- (void)setupScreen:(UILabel *)label { + label.text = @"Unlocalized"; +} +@end From 21d2c4e37aac50cebb1aac48b22d4c1b94e8a23c Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Mon, 1 Feb 2016 21:28:33 +0000 Subject: [PATCH 089/742] Code clean up; NFC. Patch by Alexander Riccio. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259409 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 16c73d924ef24e5545145d673a9f0c80257246ef) --- lib/StaticAnalyzer/Core/CheckerRegistry.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/StaticAnalyzer/Core/CheckerRegistry.cpp b/lib/StaticAnalyzer/Core/CheckerRegistry.cpp index a15e1573e22..ba03e2f8a3c 100644 --- a/lib/StaticAnalyzer/Core/CheckerRegistry.cpp +++ b/lib/StaticAnalyzer/Core/CheckerRegistry.cpp @@ -49,12 +49,12 @@ static void collectCheckers(const CheckerRegistry::CheckerInfoList &checkers, CheckerOptInfo &opt, CheckerInfoSet &collected) { // Use a binary search to find the possible start of the package. CheckerRegistry::CheckerInfo packageInfo(nullptr, opt.getName(), ""); - CheckerRegistry::CheckerInfoList::const_iterator e = checkers.end(); + auto end = checkers.cend(); CheckerRegistry::CheckerInfoList::const_iterator i = - std::lower_bound(checkers.begin(), e, packageInfo, checkerNameLT); + std::lower_bound(checkers.cbegin(), end, packageInfo, checkerNameLT); // If we didn't even find a possible package, give up. - if (i == e) + if (i == end) return; // If what we found doesn't actually start the package, give up. @@ -73,7 +73,7 @@ static void collectCheckers(const CheckerRegistry::CheckerInfoList &checkers, size = packageSize->getValue(); // Step through all the checkers in the package. - for (e = i+size; i != e; ++i) { + for (auto checkEnd = i+size; i != checkEnd; ++i) { if (opt.isEnabled()) collected.insert(&*i); else From 2b184a4cde2d564326fcec5d2b771c987f29dade Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Fri, 29 Jan 2016 15:53:43 -0800 Subject: [PATCH 090/742] Introduce ns_error_domain attribute. ns_error_domain can be used by, e.g. NS_ERROR_ENUM, in order to identify a global declaration representing the domain constant. Introduces the attribute, Sema handling, diagnostics, and test case. --- include/clang/Basic/Attr.td | 6 ++++ include/clang/Basic/AttrDocs.td | 7 ++++ include/clang/Basic/DiagnosticSemaKinds.td | 7 ++++ lib/Sema/SemaDeclAttr.cpp | 37 +++++++++++++++++++ test/Analysis/ns_error_enum.m | 42 ++++++++++++++++++++++ 5 files changed, 99 insertions(+) create mode 100644 test/Analysis/ns_error_enum.m diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index a7e073d5cae..c6126866fed 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -1115,6 +1115,12 @@ def ObjCBridgeRelated : InheritableAttr { let Documentation = [Undocumented]; } +def NSErrorDomain : Attr { + let Spellings = [GNU<"ns_error_domain">]; + let Args = [IdentifierArgument<"ErrorDomain">]; + let Documentation = [NSErrorDomainDocs]; +} + def NSReturnsRetained : InheritableAttr { let Spellings = [GNU<"ns_returns_retained">]; // let Subjects = SubjectList<[ObjCMethod, ObjCProperty, Function]>; diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td index 5c4473d08d9..08cd99b9867 100644 --- a/include/clang/Basic/AttrDocs.td +++ b/include/clang/Basic/AttrDocs.td @@ -1758,6 +1758,13 @@ arguments, with arbitrary offsets. }]; } +def NSErrorDomainDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``ns_error_domain`` attribute indicates a global constant representing the error domain. + }]; +} + def SwiftDocs : DocumentationCategory<"Controlling Swift Import"> { let Content = [{ Clang supports additional attributes for controlling how APIs are imported into Swift. diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 33d2a0e98c3..dd9fb576255 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -7514,6 +7514,13 @@ def err_nsconsumed_attribute_mismatch : Error< def err_nsreturns_retained_attribute_mismatch : Error< "overriding method has mismatched ns_returns_%select{not_retained|retained}0" " attributes">; + +def err_nserrordomain_not_tagdecl : Error< + "ns_error_domain attribute only valid on enum/struct/union/class">; +def err_nserrordomain_invalid_decl : Error< + "domain argument %0 not valid top-level declaration">; +def err_nserrordomain_requires_identifier : Error< + "domain argument must be an identifier">; def note_getter_unavailable : Note< "or because setter is declared here, but no getter method %0 is found">; diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index c1c4d1e75a5..d58fdc7c13f 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -4214,6 +4214,39 @@ static void handleObjCRequiresSuperAttr(Sema &S, Decl *D, attr.getAttributeSpellingListIndex())); } +static void handleNSErrorDomain(Sema &S, Decl *D, const AttributeList &Attr) { + if (!isa(D)) { + S.Diag(D->getLocStart(), diag::err_nserrordomain_not_tagdecl); + return; + } + IdentifierLoc *identLoc = + Attr.isArgIdent(0) ? Attr.getArgAsIdent(0) : nullptr; + if (!identLoc || !identLoc->Ident) { + // Try to locate the argument directly + SourceLocation loc = Attr.getLoc(); + if (Attr.isArgExpr(0) && Attr.getArgAsExpr(0)) + loc = Attr.getArgAsExpr(0)->getLocStart(); + + S.Diag(loc, diag::err_nserrordomain_requires_identifier); + return; + } + + // Verify that the identifier is a valid decl in the C decl namespace + LookupResult lookupResult(S, DeclarationName(identLoc->Ident), + SourceLocation(), + Sema::LookupNameKind::LookupOrdinaryName); + if (!S.LookupName(lookupResult, S.TUScope) || + !lookupResult.getAsSingle()) { + S.Diag(identLoc->Loc, diag::err_nserrordomain_invalid_decl) + << identLoc->Ident; + return; + } + + D->addAttr(::new (S.Context) + NSErrorDomainAttr(Attr.getRange(), S.Context, identLoc->Ident, + Attr.getAttributeSpellingListIndex())); +} + static void handleCFAuditedTransferAttr(Sema &S, Decl *D, const AttributeList &Attr) { if (checkAttrMutualExclusion(S, D, Attr.getRange(), @@ -5509,6 +5542,10 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_ObjCBoxable: handleObjCBoxable(S, D, Attr); break; + + case AttributeList::AT_NSErrorDomain: + handleNSErrorDomain(S, D, Attr); + break; case AttributeList::AT_CFAuditedTransfer: handleCFAuditedTransferAttr(S, D, Attr); diff --git a/test/Analysis/ns_error_enum.m b/test/Analysis/ns_error_enum.m new file mode 100644 index 00000000000..79e3c25cbaa --- /dev/null +++ b/test/Analysis/ns_error_enum.m @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -verify %s + +#define CF_ENUM(_type, _name) enum _name : _type _name; enum _name : _type +#define NS_ENUM(_type, _name) CF_ENUM(_type, _name) + +#define NS_ERROR_ENUM(_type, _name, _domain) \ + enum _name : _type _name; enum __attribute__((ns_error_domain(_domain))) _name : _type + +typedef NS_ENUM(unsigned, MyEnum) { + MyFirst, + MySecond, +}; + +typedef NS_ENUM(invalidType, MyInvalidEnum) { +// expected-error@-1{{unknown type name 'invalidType'}} +// expected-error@-2{{unknown type name 'invalidType'}} + MyFirstInvalid, + MySecondInvalid, +}; + +const char *MyErrorDomain; +typedef NS_ERROR_ENUM(unsigned char, MyErrorEnum, MyErrorDomain) { + MyErrFirst, + MyErrSecond, +}; +struct __attribute__((ns_error_domain(MyErrorDomain))) MyStructErrorDomain {}; + +typedef NS_ERROR_ENUM(unsigned char, MyErrorEnumInvalid, InvalidDomain) { + // expected-error@-1{{domain argument 'InvalidDomain' not valid top-level declaration}} + MyErrFirstInvalid, + MyErrSecondInvalid, +}; + +typedef NS_ERROR_ENUM(unsigned char, MyErrorEnumInvalid, "domain-string"); + // expected-error@-1{{domain argument must be an identifier}} + +int __attribute__((ns_error_domain(MyErrorDomain))) NotTagDecl; + // expected-error@-1{{ns_error_domain attribute only valid on enum/struct/union/class}} + +void foo() {} +typedef NS_ERROR_ENUM(unsigned char, MyErrorEnumInvalidFunction, foo); + // expected-error@-1{{domain argument 'foo' not valid top-level declaration}} From 62946e0fd3161928729f6d56ff1717324687dd54 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Sun, 7 Feb 2016 18:21:28 +0000 Subject: [PATCH 091/742] [libclang] Add missing CINDEX_LINKAGE from a function. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260047 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang-c/Index.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h index 69a98d7978c..4ecbed5f19a 100644 --- a/include/clang-c/Index.h +++ b/include/clang-c/Index.h @@ -3640,8 +3640,8 @@ typedef enum CXChildVisitResult * Visits the children of a cursor using the specified block. Behaves * identically to clang_visitChildren() in all other respects. */ -unsigned clang_visitChildrenWithBlock(CXCursor parent, - CXCursorVisitorBlock block); +CINDEX_LINKAGE unsigned clang_visitChildrenWithBlock(CXCursor parent, + CXCursorVisitorBlock block); # endif #endif From cce15c3c210e52ccb27951d0d2f924acacc77192 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Sun, 7 Feb 2016 19:28:36 +0000 Subject: [PATCH 092/742] [Frontend] Make the memory management of FrontendAction pointers explicit by using unique_ptr. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260048 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/ARCMigrate/ARCMTActions.h | 11 +-- include/clang/Frontend/FrontendAction.h | 2 +- include/clang/Frontend/FrontendActions.h | 5 +- .../clang/Rewrite/Frontend/FrontendActions.h | 4 +- lib/ARCMigrate/ARCMTActions.cpp | 12 +-- lib/ARCMigrate/ObjCMT.cpp | 5 +- lib/Frontend/ASTMerge.cpp | 5 +- lib/Frontend/FrontendAction.cpp | 5 +- .../ExecuteCompilerInvocation.cpp | 89 ++++++++++--------- 9 files changed, 73 insertions(+), 65 deletions(-) diff --git a/include/clang/ARCMigrate/ARCMTActions.h b/include/clang/ARCMigrate/ARCMTActions.h index c830aa3d787..554e0c0c6d0 100644 --- a/include/clang/ARCMigrate/ARCMTActions.h +++ b/include/clang/ARCMigrate/ARCMTActions.h @@ -22,7 +22,7 @@ class CheckAction : public WrapperFrontendAction { bool BeginInvocation(CompilerInstance &CI) override; public: - CheckAction(FrontendAction *WrappedAction); + CheckAction(std::unique_ptr WrappedAction); }; class ModifyAction : public WrapperFrontendAction { @@ -30,7 +30,7 @@ class ModifyAction : public WrapperFrontendAction { bool BeginInvocation(CompilerInstance &CI) override; public: - ModifyAction(FrontendAction *WrappedAction); + ModifyAction(std::unique_ptr WrappedAction); }; class MigrateSourceAction : public ASTFrontendAction { @@ -49,7 +49,8 @@ class MigrateAction : public WrapperFrontendAction { bool BeginInvocation(CompilerInstance &CI) override; public: - MigrateAction(FrontendAction *WrappedAction, StringRef migrateDir, + MigrateAction(std::unique_ptr WrappedAction, + StringRef migrateDir, StringRef plistOut, bool emitPremigrationARCErrors); }; @@ -61,8 +62,8 @@ class ObjCMigrateAction : public WrapperFrontendAction { FileRemapper Remapper; CompilerInstance *CompInst; public: - ObjCMigrateAction(FrontendAction *WrappedAction, StringRef migrateDir, - unsigned migrateAction); + ObjCMigrateAction(std::unique_ptr WrappedAction, + StringRef migrateDir, unsigned migrateAction); protected: std::unique_ptr CreateASTConsumer(CompilerInstance &CI, diff --git a/include/clang/Frontend/FrontendAction.h b/include/clang/Frontend/FrontendAction.h index c407ff80ac5..1b021ef9e9f 100644 --- a/include/clang/Frontend/FrontendAction.h +++ b/include/clang/Frontend/FrontendAction.h @@ -283,7 +283,7 @@ class WrapperFrontendAction : public FrontendAction { public: /// Construct a WrapperFrontendAction from an existing action, taking /// ownership of it. - WrapperFrontendAction(FrontendAction *WrappedAction); + WrapperFrontendAction(std::unique_ptr WrappedAction); bool usesPreprocessorOnly() const override; TranslationUnitKind getTranslationUnitKind() override; diff --git a/include/clang/Frontend/FrontendActions.h b/include/clang/Frontend/FrontendActions.h index f61775f014f..025955dd5ef 100644 --- a/include/clang/Frontend/FrontendActions.h +++ b/include/clang/Frontend/FrontendActions.h @@ -168,7 +168,7 @@ class VerifyPCHAction : public ASTFrontendAction { */ class ASTMergeAction : public FrontendAction { /// \brief The action that the merge action adapts. - FrontendAction *AdaptedAction; + std::unique_ptr AdaptedAction; /// \brief The set of AST files to merge. std::vector ASTFiles; @@ -184,7 +184,8 @@ class ASTMergeAction : public FrontendAction { void EndSourceFileAction() override; public: - ASTMergeAction(FrontendAction *AdaptedAction, ArrayRef ASTFiles); + ASTMergeAction(std::unique_ptr AdaptedAction, + ArrayRef ASTFiles); ~ASTMergeAction() override; bool usesPreprocessorOnly() const override; diff --git a/include/clang/Rewrite/Frontend/FrontendActions.h b/include/clang/Rewrite/Frontend/FrontendActions.h index 6c290e4d605..27976eac4ed 100644 --- a/include/clang/Rewrite/Frontend/FrontendActions.h +++ b/include/clang/Rewrite/Frontend/FrontendActions.h @@ -50,8 +50,8 @@ class FixItAction : public ASTFrontendAction { /// frontend action. class FixItRecompile : public WrapperFrontendAction { public: - FixItRecompile(FrontendAction *WrappedAction) - : WrapperFrontendAction(WrappedAction) {} + FixItRecompile(std::unique_ptr WrappedAction) + : WrapperFrontendAction(std::move(WrappedAction)) {} protected: bool BeginInvocation(CompilerInstance &CI) override; diff --git a/lib/ARCMigrate/ARCMTActions.cpp b/lib/ARCMigrate/ARCMTActions.cpp index 39a922f426c..0a5473ab19e 100644 --- a/lib/ARCMigrate/ARCMTActions.cpp +++ b/lib/ARCMigrate/ARCMTActions.cpp @@ -25,8 +25,8 @@ bool CheckAction::BeginInvocation(CompilerInstance &CI) { return true; } -CheckAction::CheckAction(FrontendAction *WrappedAction) - : WrapperFrontendAction(WrappedAction) {} +CheckAction::CheckAction(std::unique_ptr WrappedAction) + : WrapperFrontendAction(std::move(WrappedAction)) {} bool ModifyAction::BeginInvocation(CompilerInstance &CI) { return !arcmt::applyTransformations(CI.getInvocation(), getCurrentInput(), @@ -34,8 +34,8 @@ bool ModifyAction::BeginInvocation(CompilerInstance &CI) { CI.getDiagnostics().getClient()); } -ModifyAction::ModifyAction(FrontendAction *WrappedAction) - : WrapperFrontendAction(WrappedAction) {} +ModifyAction::ModifyAction(std::unique_ptr WrappedAction) + : WrapperFrontendAction(std::move(WrappedAction)) {} bool MigrateAction::BeginInvocation(CompilerInstance &CI) { if (arcmt::migrateWithTemporaryFiles( @@ -49,11 +49,11 @@ bool MigrateAction::BeginInvocation(CompilerInstance &CI) { return true; } -MigrateAction::MigrateAction(FrontendAction *WrappedAction, +MigrateAction::MigrateAction(std::unique_ptr WrappedAction, StringRef migrateDir, StringRef plistOut, bool emitPremigrationARCErrors) - : WrapperFrontendAction(WrappedAction), MigrateDir(migrateDir), + : WrapperFrontendAction(std::move(WrappedAction)), MigrateDir(migrateDir), PlistOut(plistOut), EmitPremigrationARCErros(emitPremigrationARCErrors) { if (MigrateDir.empty()) MigrateDir = "."; // user current directory if none is given. diff --git a/lib/ARCMigrate/ObjCMT.cpp b/lib/ARCMigrate/ObjCMT.cpp index 1be724c38af..3737914a7cf 100644 --- a/lib/ARCMigrate/ObjCMT.cpp +++ b/lib/ARCMigrate/ObjCMT.cpp @@ -179,10 +179,11 @@ class ObjCMigrateASTConsumer : public ASTConsumer { } -ObjCMigrateAction::ObjCMigrateAction(FrontendAction *WrappedAction, +ObjCMigrateAction::ObjCMigrateAction( + std::unique_ptr WrappedAction, StringRef migrateDir, unsigned migrateAction) - : WrapperFrontendAction(WrappedAction), MigrateDir(migrateDir), + : WrapperFrontendAction(std::move(WrappedAction)), MigrateDir(migrateDir), ObjCMigAction(migrateAction), CompInst(nullptr) { if (MigrateDir.empty()) diff --git a/lib/Frontend/ASTMerge.cpp b/lib/Frontend/ASTMerge.cpp index b499fa2b0e6..51064da270c 100644 --- a/lib/Frontend/ASTMerge.cpp +++ b/lib/Frontend/ASTMerge.cpp @@ -83,14 +83,13 @@ void ASTMergeAction::EndSourceFileAction() { return AdaptedAction->EndSourceFileAction(); } -ASTMergeAction::ASTMergeAction(FrontendAction *AdaptedAction, +ASTMergeAction::ASTMergeAction(std::unique_ptr adaptedAction, ArrayRef ASTFiles) - : AdaptedAction(AdaptedAction), ASTFiles(ASTFiles.begin(), ASTFiles.end()) { +: AdaptedAction(std::move(adaptedAction)), ASTFiles(ASTFiles.begin(), ASTFiles.end()) { assert(AdaptedAction && "ASTMergeAction needs an action to adapt"); } ASTMergeAction::~ASTMergeAction() { - delete AdaptedAction; } bool ASTMergeAction::usesPreprocessorOnly() const { diff --git a/lib/Frontend/FrontendAction.cpp b/lib/Frontend/FrontendAction.cpp index 005bbf6e274..71fb17c25f5 100644 --- a/lib/Frontend/FrontendAction.cpp +++ b/lib/Frontend/FrontendAction.cpp @@ -594,6 +594,7 @@ bool WrapperFrontendAction::hasCodeCompletionSupport() const { return WrappedAction->hasCodeCompletionSupport(); } -WrapperFrontendAction::WrapperFrontendAction(FrontendAction *WrappedAction) - : WrappedAction(WrappedAction) {} +WrapperFrontendAction::WrapperFrontendAction( + std::unique_ptr WrappedAction) + : WrappedAction(std::move(WrappedAction)) {} diff --git a/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/lib/FrontendTool/ExecuteCompilerInvocation.cpp index 79cf0049a7b..116590e5375 100644 --- a/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -31,33 +31,34 @@ using namespace clang; using namespace llvm::opt; -static FrontendAction *CreateFrontendBaseAction(CompilerInstance &CI) { +static std::unique_ptr +CreateFrontendBaseAction(CompilerInstance &CI) { using namespace clang::frontend; StringRef Action("unknown"); (void)Action; switch (CI.getFrontendOpts().ProgramAction) { - case ASTDeclList: return new ASTDeclListAction(); - case ASTDump: return new ASTDumpAction(); - case ASTPrint: return new ASTPrintAction(); - case ASTView: return new ASTViewAction(); - case DumpRawTokens: return new DumpRawTokensAction(); - case DumpTokens: return new DumpTokensAction(); - case EmitAssembly: return new EmitAssemblyAction(); - case EmitBC: return new EmitBCAction(); - case EmitHTML: return new HTMLPrintAction(); - case EmitLLVM: return new EmitLLVMAction(); - case EmitLLVMOnly: return new EmitLLVMOnlyAction(); - case EmitCodeGenOnly: return new EmitCodeGenOnlyAction(); - case EmitObj: return new EmitObjAction(); - case FixIt: return new FixItAction(); - case GenerateModule: return new GenerateModuleAction; - case GeneratePCH: return new GeneratePCHAction; - case GeneratePTH: return new GeneratePTHAction(); - case InitOnly: return new InitOnlyAction(); - case ParseSyntaxOnly: return new SyntaxOnlyAction(); - case ModuleFileInfo: return new DumpModuleInfoAction(); - case VerifyPCH: return new VerifyPCHAction(); + case ASTDeclList: return llvm::make_unique(); + case ASTDump: return llvm::make_unique(); + case ASTPrint: return llvm::make_unique(); + case ASTView: return llvm::make_unique(); + case DumpRawTokens: return llvm::make_unique(); + case DumpTokens: return llvm::make_unique(); + case EmitAssembly: return llvm::make_unique(); + case EmitBC: return llvm::make_unique(); + case EmitHTML: return llvm::make_unique(); + case EmitLLVM: return llvm::make_unique(); + case EmitLLVMOnly: return llvm::make_unique(); + case EmitCodeGenOnly: return llvm::make_unique(); + case EmitObj: return llvm::make_unique(); + case FixIt: return llvm::make_unique(); + case GenerateModule: return llvm::make_unique(); + case GeneratePCH: return llvm::make_unique(); + case GeneratePTH: return llvm::make_unique(); + case InitOnly: return llvm::make_unique(); + case ParseSyntaxOnly: return llvm::make_unique(); + case ModuleFileInfo: return llvm::make_unique(); + case VerifyPCH: return llvm::make_unique(); case PluginAction: { for (FrontendPluginRegistry::iterator it = @@ -67,7 +68,7 @@ static FrontendAction *CreateFrontendBaseAction(CompilerInstance &CI) { std::unique_ptr P(it->instantiate()); if (!P->ParseArgs(CI, CI.getFrontendOpts().PluginArgs)) return nullptr; - return P.release(); + return std::move(P); } } @@ -76,32 +77,33 @@ static FrontendAction *CreateFrontendBaseAction(CompilerInstance &CI) { return nullptr; } - case PrintDeclContext: return new DeclContextPrintAction(); - case PrintPreamble: return new PrintPreambleAction(); + case PrintDeclContext: return llvm::make_unique(); + case PrintPreamble: return llvm::make_unique(); case PrintPreprocessedInput: { if (CI.getPreprocessorOutputOpts().RewriteIncludes) - return new RewriteIncludesAction(); - return new PrintPreprocessedAction(); + return llvm::make_unique(); + return llvm::make_unique(); } - case RewriteMacros: return new RewriteMacrosAction(); - case RewriteTest: return new RewriteTestAction(); + case RewriteMacros: return llvm::make_unique(); + case RewriteTest: return llvm::make_unique(); #ifdef CLANG_ENABLE_OBJC_REWRITER - case RewriteObjC: return new RewriteObjCAction(); + case RewriteObjC: return llvm::make_unique(); #else case RewriteObjC: Action = "RewriteObjC"; break; #endif #ifdef CLANG_ENABLE_ARCMT - case MigrateSource: return new arcmt::MigrateSourceAction(); + case MigrateSource: + return llvm::make_unique(); #else case MigrateSource: Action = "MigrateSource"; break; #endif #ifdef CLANG_ENABLE_STATIC_ANALYZER - case RunAnalysis: return new ento::AnalysisAction(); + case RunAnalysis: return llvm::make_unique(); #else case RunAnalysis: Action = "RunAnalysis"; break; #endif - case RunPreprocessorOnly: return new PreprocessOnlyAction(); + case RunPreprocessorOnly: return llvm::make_unique(); } #if !defined(CLANG_ENABLE_ARCMT) || !defined(CLANG_ENABLE_STATIC_ANALYZER) \ @@ -113,16 +115,17 @@ static FrontendAction *CreateFrontendBaseAction(CompilerInstance &CI) { #endif } -static FrontendAction *CreateFrontendAction(CompilerInstance &CI) { +static std::unique_ptr +CreateFrontendAction(CompilerInstance &CI) { // Create the underlying action. - FrontendAction *Act = CreateFrontendBaseAction(CI); + std::unique_ptr Act = CreateFrontendBaseAction(CI); if (!Act) return nullptr; const FrontendOptions &FEOpts = CI.getFrontendOpts(); if (FEOpts.FixAndRecompile) { - Act = new FixItRecompile(Act); + Act = llvm::make_unique(std::move(Act)); } #ifdef CLANG_ENABLE_ARCMT @@ -133,13 +136,13 @@ static FrontendAction *CreateFrontendAction(CompilerInstance &CI) { case FrontendOptions::ARCMT_None: break; case FrontendOptions::ARCMT_Check: - Act = new arcmt::CheckAction(Act); + Act = llvm::make_unique(std::move(Act)); break; case FrontendOptions::ARCMT_Modify: - Act = new arcmt::ModifyAction(Act); + Act = llvm::make_unique(std::move(Act)); break; case FrontendOptions::ARCMT_Migrate: - Act = new arcmt::MigrateAction(Act, + Act = llvm::make_unique(std::move(Act), FEOpts.MTMigrateDir, FEOpts.ARCMTMigrateReportOut, FEOpts.ARCMTMigrateEmitARCErrors); @@ -147,8 +150,9 @@ static FrontendAction *CreateFrontendAction(CompilerInstance &CI) { } if (FEOpts.ObjCMTAction != FrontendOptions::ObjCMT_None) { - Act = new arcmt::ObjCMigrateAction(Act, FEOpts.MTMigrateDir, - FEOpts.ObjCMTAction); + Act = llvm::make_unique(std::move(Act), + FEOpts.MTMigrateDir, + FEOpts.ObjCMTAction); } } #endif @@ -156,7 +160,8 @@ static FrontendAction *CreateFrontendAction(CompilerInstance &CI) { // If there are any AST files to merge, create a frontend action // adaptor to perform the merge. if (!FEOpts.ASTMergeFiles.empty()) - Act = new ASTMergeAction(Act, FEOpts.ASTMergeFiles); + Act = llvm::make_unique(std::move(Act), + FEOpts.ASTMergeFiles); return Act; } From 241af697eee1c7dfc7e42fed1b08eeb41574ccda Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Sat, 6 Feb 2016 17:17:32 +0000 Subject: [PATCH 093/742] [analyzer] DeallocChecker: Don't warn on release of readonly assign property in dealloc. It is common for the ivars for read-only assign properties to always be stored retained, so don't warn for a release in dealloc for the ivar backing these properties. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259998 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 44272b9ce7be2c5a2b81a81cb2383b07ad3948d8) --- lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp | 6 ++++++ test/Analysis/PR2978.m | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index d17a8b53978..1a3519307a2 100644 --- a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -216,6 +216,12 @@ static void checkObjCDealloc(const CheckerBase *Checker, << "' was retained by a synthesized property " "but was not released in 'dealloc'"; } else { + // It is common for the ivars for read-only assign properties to + // always be stored retained, so don't warn for a release in + // dealloc for the ivar backing these properties. + if (PD->isReadOnly()) + continue; + name = LOpts.getGC() == LangOptions::NonGC ? "extra ivar release (use-after-release)" : "extra ivar release (Hybrid MM, non-GC)"; diff --git a/test/Analysis/PR2978.m b/test/Analysis/PR2978.m index 6d5a6dfefc2..1fbd9723a2b 100644 --- a/test/Analysis/PR2978.m +++ b/test/Analysis/PR2978.m @@ -39,7 +39,7 @@ @implementation MyClass @synthesize Z = _Z; // expected-warning{{The '_Z' instance variable in 'MyClass' was not retained by a synthesized property but was released in 'dealloc'}} @synthesize K = _K; @synthesize L = _L; // no-warning -@synthesize N = _N; // expected-warning{{The '_N' instance variable in 'MyClass' was not retained by a synthesized property but was released in 'dealloc'}} +@synthesize N = _N; // no-warning @synthesize M = _M; @synthesize V = _V; @synthesize W = _W; // expected-warning{{The '_W' instance variable in 'MyClass' was retained by a synthesized property but was not released in 'dealloc'}} From 06e1c08492c2d1dbadff7688988c88bc332f0dc8 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Sun, 7 Feb 2016 16:55:44 +0000 Subject: [PATCH 094/742] [analyzer] Invalidate destination of std::copy() and std::copy_backward(). Now that the libcpp implementations of these methods has a branch that doesn't call memmove(), the analyzer needs to invalidate the destination for these methods explicitly. rdar://problem/23575656 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260043 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 8b5ad43a09275893b6255d46bcf6c19042f96b25) --- include/clang/Analysis/AnalysisContext.h | 4 + lib/Analysis/AnalysisDeclContext.cpp | 15 +++ .../Checkers/CStringChecker.cpp | 58 +++++++++ .../Core/BugReporterVisitors.cpp | 16 +-- .../Core/ExprEngineCallAndReturn.cpp | 17 +-- .../Inputs/system-header-simulator-cxx.h | 118 +++++++++++++++++- test/Analysis/bstring.cpp | 38 ++++++ .../diagnostics/explicit-suppression.cpp | 12 +- 8 files changed, 241 insertions(+), 37 deletions(-) create mode 100644 test/Analysis/bstring.cpp diff --git a/include/clang/Analysis/AnalysisContext.h b/include/clang/Analysis/AnalysisContext.h index 931190e43a6..4324a0352e3 100644 --- a/include/clang/Analysis/AnalysisContext.h +++ b/include/clang/Analysis/AnalysisContext.h @@ -201,6 +201,10 @@ class AnalysisDeclContext { } return static_cast(data); } + + /// Returns true if the root namespace of the given declaration is the 'std' + /// C++ namespace. + static bool isInStdNamespace(const Decl *D); private: ManagedAnalysis *&getAnalysisImpl(const void* tag); diff --git a/lib/Analysis/AnalysisDeclContext.cpp b/lib/Analysis/AnalysisDeclContext.cpp index 52c7f261365..94f753ed912 100644 --- a/lib/Analysis/AnalysisDeclContext.cpp +++ b/lib/Analysis/AnalysisDeclContext.cpp @@ -317,6 +317,21 @@ AnalysisDeclContext::getBlockInvocationContext(const LocationContext *parent, BD, ContextData); } +bool AnalysisDeclContext::isInStdNamespace(const Decl *D) { + const DeclContext *DC = D->getDeclContext()->getEnclosingNamespaceContext(); + const NamespaceDecl *ND = dyn_cast(DC); + if (!ND) + return false; + + while (const DeclContext *Parent = ND->getParent()) { + if (!isa(Parent)) + break; + ND = cast(Parent); + } + + return ND->isStdNamespace(); +} + LocationContextManager & AnalysisDeclContext::getLocationContextManager() { assert(Manager && "Cannot create LocationContexts without an AnalysisDeclContextManager!"); diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index 17537445d66..5130dd610ae 100644 --- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -118,6 +118,10 @@ class CStringChecker : public Checker< eval::Call, void evalStrsep(CheckerContext &C, const CallExpr *CE) const; + void evalStdCopy(CheckerContext &C, const CallExpr *CE) const; + void evalStdCopyBackward(CheckerContext &C, const CallExpr *CE) const; + void evalStdCopyCommon(CheckerContext &C, const CallExpr *CE) const; + // Utility methods std::pair static assumeZero(CheckerContext &C, @@ -1950,7 +1954,57 @@ void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const { C.addTransition(State); } +// These should probably be moved into a C++ standard library checker. +void CStringChecker::evalStdCopy(CheckerContext &C, const CallExpr *CE) const { + evalStdCopyCommon(C, CE); +} + +void CStringChecker::evalStdCopyBackward(CheckerContext &C, + const CallExpr *CE) const { + evalStdCopyCommon(C, CE); +} + +void CStringChecker::evalStdCopyCommon(CheckerContext &C, + const CallExpr *CE) const { + if (CE->getNumArgs() < 3) + return; + + ProgramStateRef State = C.getState(); + + const LocationContext *LCtx = C.getLocationContext(); + + // template + // _OutputIterator + // copy(_InputIterator __first, _InputIterator __last, + // _OutputIterator __result) + + // Invalidate the destination buffer + const Expr *Dst = CE->getArg(2); + SVal DstVal = State->getSVal(Dst, LCtx); + State = InvalidateBuffer(C, State, Dst, DstVal, /*IsSource=*/false, + /*Size=*/nullptr); + + SValBuilder &SVB = C.getSValBuilder(); + + SVal ResultVal = SVB.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount()); + State = State->BindExpr(CE, LCtx, ResultVal); + + C.addTransition(State); +} + +static bool isCPPStdLibraryFunction(const FunctionDecl *FD, StringRef Name) { + IdentifierInfo *II = FD->getIdentifier(); + if (!II) + return false; + + if (!AnalysisDeclContext::isInStdNamespace(FD)) + return false; + if (II->getName().equals(Name)) + return true; + + return false; +} //===----------------------------------------------------------------------===// // The driver method, and other Checker callbacks. //===----------------------------------------------------------------------===// @@ -1999,6 +2053,10 @@ bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { evalFunction = &CStringChecker::evalBcopy; else if (C.isCLibraryFunction(FDecl, "bcmp")) evalFunction = &CStringChecker::evalMemcmp; + else if (isCPPStdLibraryFunction(FDecl, "copy")) + evalFunction = &CStringChecker::evalStdCopy; + else if (isCPPStdLibraryFunction(FDecl, "copy_backward")) + evalFunction = &CStringChecker::evalStdCopyBackward; // If the callee isn't a string function, let another checker handle it. if (!evalFunction) diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index ae5cd546f8d..859a2220e09 100644 --- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -1540,20 +1540,6 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, return event; } - -// FIXME: Copied from ExprEngineCallAndReturn.cpp. -static bool isInStdNamespace(const Decl *D) { - const DeclContext *DC = D->getDeclContext()->getEnclosingNamespaceContext(); - const NamespaceDecl *ND = dyn_cast(DC); - if (!ND) - return false; - - while (const NamespaceDecl *Parent = dyn_cast(ND->getParent())) - ND = Parent; - - return ND->isStdNamespace(); -} - std::unique_ptr LikelyFalsePositiveSuppressionBRVisitor::getEndPath(BugReporterContext &BRC, const ExplodedNode *N, @@ -1564,7 +1550,7 @@ LikelyFalsePositiveSuppressionBRVisitor::getEndPath(BugReporterContext &BRC, AnalyzerOptions &Options = Eng.getAnalysisManager().options; const Decl *D = N->getLocationContext()->getDecl(); - if (isInStdNamespace(D)) { + if (AnalysisDeclContext::isInStdNamespace(D)) { // Skip reports within the 'std' namespace. Although these can sometimes be // the user's fault, we currently don't report them very well, and // Note that this will not help for any other data structure libraries, like diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 74cc8d2ccbc..2c24dc1353a 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -382,21 +382,6 @@ void ExprEngine::examineStackFrames(const Decl *D, const LocationContext *LCtx, } -static bool IsInStdNamespace(const FunctionDecl *FD) { - const DeclContext *DC = FD->getEnclosingNamespaceContext(); - const NamespaceDecl *ND = dyn_cast(DC); - if (!ND) - return false; - - while (const DeclContext *Parent = ND->getParent()) { - if (!isa(Parent)) - break; - ND = cast(Parent); - } - - return ND->isStdNamespace(); -} - // The GDM component containing the dynamic dispatch bifurcation info. When // the exact type of the receiver is not known, we want to explore both paths - // one on which we do inline it and the other one on which we don't. This is @@ -761,7 +746,7 @@ static bool mayInlineDecl(AnalysisDeclContext *CalleeADC, // Conditionally control the inlining of C++ standard library functions. if (!Opts.mayInlineCXXStandardLibrary()) if (Ctx.getSourceManager().isInSystemHeader(FD->getLocation())) - if (IsInStdNamespace(FD)) + if (AnalysisDeclContext::isInStdNamespace(FD)) return false; // Conditionally control the inlining of methods on objects that look diff --git a/test/Analysis/Inputs/system-header-simulator-cxx.h b/test/Analysis/Inputs/system-header-simulator-cxx.h index f9049c3ae9e..f6b970088ea 100644 --- a/test/Analysis/Inputs/system-header-simulator-cxx.h +++ b/test/Analysis/Inputs/system-header-simulator-cxx.h @@ -7,6 +7,9 @@ typedef unsigned char uint8_t; +typedef __typeof__(sizeof(int)) size_t; +void *memmove(void *s1, const void *s2, size_t n); + namespace std { template struct pair { @@ -104,11 +107,120 @@ namespace std { const _E* end() const {return __begin_ + __size_;} }; + template struct enable_if {}; + template struct enable_if {typedef _Tp type;}; + + template + struct integral_constant + { + static const _Tp value = __v; + typedef _Tp value_type; + typedef integral_constant type; + + operator value_type() const {return value;} + + value_type operator ()() const {return value;} + }; + + template + const _Tp integral_constant<_Tp, __v>::value; + + template + struct is_trivially_assignable + : integral_constant + { + }; + + typedef integral_constant true_type; + typedef integral_constant false_type; + + template struct is_const : public false_type {}; + template struct is_const<_Tp const> : public true_type {}; + + template struct is_reference : public false_type {}; + template struct is_reference<_Tp&> : public true_type {}; + + template struct is_same : public false_type {}; + template struct is_same<_Tp, _Tp> : public true_type {}; + + template ::value || is_reference<_Tp>::value > + struct __add_const {typedef _Tp type;}; + + template + struct __add_const<_Tp, false> {typedef const _Tp type;}; + + template struct add_const {typedef typename __add_const<_Tp>::type type;}; + + template struct remove_const {typedef _Tp type;}; + template struct remove_const {typedef _Tp type;}; + + template struct add_lvalue_reference {typedef _Tp& type;}; + + template struct is_trivially_copy_assignable + : public is_trivially_assignable::type, + typename add_lvalue_reference::type>::type> {}; + + template + OutputIter __copy(InputIter II, InputIter IE, OutputIter OI) { + while (II != IE) + *OI++ = *II++; + + return OI; + } + + template + inline + typename enable_if + < + is_same::type, _Up>::value && + is_trivially_copy_assignable<_Up>::value, + _Up* + >::type __copy(_Tp* __first, _Tp* __last, _Up* __result) { + size_t __n = __last - __first; + + if (__n > 0) + memmove(__result, __first, __n * sizeof(_Up)); + + return __result + __n; + } + template OutputIter copy(InputIter II, InputIter IE, OutputIter OI) { - while (II != IE) - *OI++ = *II++; - return OI; + return __copy(II, IE, OI); + } + + template + inline + _OutputIterator + __copy_backward(_BidirectionalIterator __first, _BidirectionalIterator __last, + _OutputIterator __result) + { + while (__first != __last) + *--__result = *--__last; + return __result; + } + + template + inline + typename enable_if + < + is_same::type, _Up>::value && + is_trivially_copy_assignable<_Up>::value, + _Up* + >::type __copy_backward(_Tp* __first, _Tp* __last, _Up* __result) { + size_t __n = __last - __first; + + if (__n > 0) + { + __result -= __n; + memmove(__result, __first, __n * sizeof(_Up)); + } + return __result; + } + + template + OutputIter copy_backward(InputIter II, InputIter IE, OutputIter OI) { + return __copy_backward(II, IE, OI); } struct input_iterator_tag { }; diff --git a/test/Analysis/bstring.cpp b/test/Analysis/bstring.cpp new file mode 100644 index 00000000000..0b4e7e94f00 --- /dev/null +++ b/test/Analysis/bstring.cpp @@ -0,0 +1,38 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.cstring,alpha.unix.cstring,debug.ExprInspection -analyzer-store=region -verify %s + +#include "Inputs/system-header-simulator-cxx.h" +#include "Inputs/system-header-simulator-for-malloc.h" + +void clang_analyzer_eval(int); + +int *testStdCopyInvalidatesBuffer(std::vector v) { + int n = v.size(); + int *buf = (int *)malloc(n * sizeof(int)); + + buf[0] = 66; + + // Call to copy should invalidate buf. + std::copy(v.begin(), v.end(), buf); + + int i = buf[0]; + + clang_analyzer_eval(i == 66); // expected-warning {{UNKNOWN}} + + return buf; +} + +int *testStdCopyBackwardInvalidatesBuffer(std::vector v) { + int n = v.size(); + int *buf = (int *)malloc(n * sizeof(int)); + + buf[0] = 66; + + // Call to copy_backward should invalidate buf. + std::copy_backward(v.begin(), v.end(), buf + n); + + int i = buf[0]; + + clang_analyzer_eval(i == 66); // expected-warning {{UNKNOWN}} + + return buf; +} diff --git a/test/Analysis/diagnostics/explicit-suppression.cpp b/test/Analysis/diagnostics/explicit-suppression.cpp index 78067573e32..67a47d02b6e 100644 --- a/test/Analysis/diagnostics/explicit-suppression.cpp +++ b/test/Analysis/diagnostics/explicit-suppression.cpp @@ -9,9 +9,15 @@ void clang_analyzer_eval(bool); -void testCopyNull(int *I, int *E) { - std::copy(I, E, (int *)0); +class C { + // The virtual function is to make C not trivially copy assignable so that we call the + // variant of std::copy() that does not defer to memmove(). + virtual int f(); +}; + +void testCopyNull(C *I, C *E) { + std::copy(I, E, (C *)0); #ifndef SUPPRESSED - // expected-warning@../Inputs/system-header-simulator-cxx.h:110 {{Dereference of null pointer}} + // expected-warning@../Inputs/system-header-simulator-cxx.h:166 {{Called C++ object pointer is null}} #endif } From 5a64e069e0f5e3045a50c3da0576bca67274642c Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Mon, 8 Feb 2016 00:28:24 +0000 Subject: [PATCH 095/742] [analyzer] Avoid crash when attempting to evaluate binary operation on LazyCompoundVal. Instead, return UnknownValue if either operand is a nonloc::LazyCompoundVal. This is a spot fix for PR 24951. rdar://problem/23682244 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260066 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit eb8ffd99ee97b7ff8ca7920ced168a2e61c67762) --- lib/StaticAnalyzer/Core/SValBuilder.cpp | 5 +++++ test/Analysis/string.c | 14 ++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/lib/StaticAnalyzer/Core/SValBuilder.cpp b/lib/StaticAnalyzer/Core/SValBuilder.cpp index 22bc14edd68..72bcdd9ecb0 100644 --- a/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -367,6 +367,11 @@ SVal SValBuilder::evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, if (lhs.isUnknown() || rhs.isUnknown()) return UnknownVal(); + if (lhs.getAs() || + rhs.getAs()) { + return UnknownVal(); + } + if (Optional LV = lhs.getAs()) { if (Optional RV = rhs.getAs()) return evalBinOpLL(state, op, *LV, *RV, type); diff --git a/test/Analysis/string.c b/test/Analysis/string.c index 9fd3efb5c2d..c65d2be1a40 100644 --- a/test/Analysis/string.c +++ b/test/Analysis/string.c @@ -756,6 +756,20 @@ void strcmp_unknown_arg (char *unknown) { clang_analyzer_eval(strcmp(unknown, unknown) == 0); // expected-warning{{TRUE}} } +union argument { + char *f; +}; + +void function_pointer_cast_helper(char **a) { + strcmp("Hi", *a); // PR24951 crash +} + +void strcmp_union_function_pointer_cast(union argument a) { + void (*fPtr)(union argument *) = (void (*)(union argument *))function_pointer_cast_helper; + + fPtr(&a); +} + //===----------------------------------------------------------------------=== // strncmp() //===----------------------------------------------------------------------=== From 6951389c5674ce8d7c38db199ea89ed7bc936745 Mon Sep 17 00:00:00 2001 From: Vedant Kumar Date: Mon, 8 Feb 2016 19:25:45 +0000 Subject: [PATCH 096/742] [Coverage] Fix crash when handling certain macro expansions When handling 'if' statements, we crash if the condition and the consequent branch are spanned by a single macro expansion. The crash occurs because of a sanity 'reset' in popRegions(): if an expansion exactly spans an entire region, we set MostRecentLocation to the start of the expansion (its 'include location'). This ensures we don't handleFileExit() ourselves out of the expansion before we're done processing all of the regions within it. This is tested in test/CoverageMapping/macro-expressions.c. This causes a problem when an expansion spans both the condition and the consequent branch of an 'if' statement. MostRecentLocation is updated to the start of the 'if' statement in popRegions(), so the file for the expansion isn't exited by the time we're done handling the statement. We then crash with 'fatal: File exit not handled before popRegions'. The fix for this is to detect these kinds of expansions, and conservatively update MostRecentLocation to the end of expansion region containing the conditional. I've added tests to make sure we don't have the same problem with other kinds of statements. rdar://problem/23630316 Differential Revision: http://reviews.llvm.org/D16934 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260129 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 61cd9c9fc26ea67faac6463837af5cd0b3806fd7) --- lib/CodeGen/CoverageMappingGen.cpp | 6 ++++ test/CoverageMapping/macro-expressions.cpp | 38 ++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/lib/CodeGen/CoverageMappingGen.cpp b/lib/CodeGen/CoverageMappingGen.cpp index 7edd7ce86ad..75b85ed8d51 100644 --- a/lib/CodeGen/CoverageMappingGen.cpp +++ b/lib/CodeGen/CoverageMappingGen.cpp @@ -434,6 +434,12 @@ struct CounterCoverageMappingBuilder Visit(S); Counter ExitCount = getRegion().getCounter(); popRegions(Index); + + // The statement may be spanned by an expansion. Make sure we handle a file + // exit out of this expansion before moving to the next statement. + if (SM.isBeforeInTranslationUnit(getStart(S), S->getLocStart())) + MostRecentLocation = getEnd(S); + return ExitCount; } diff --git a/test/CoverageMapping/macro-expressions.cpp b/test/CoverageMapping/macro-expressions.cpp index 1085ab02492..e910829b2fd 100644 --- a/test/CoverageMapping/macro-expressions.cpp +++ b/test/CoverageMapping/macro-expressions.cpp @@ -12,6 +12,44 @@ #define PRIo64 PRI_64_LENGTH_MODIFIER "o" #define PRIu64 PRI_64_LENGTH_MODIFIER "u" +#define STMT(s) s + +void fn1() { + STMT(if (1)); + STMT(while (1)); + STMT(for (;;)); + STMT(if) (1); + STMT(while) (1); + STMT(for) (;;); + if (1) + STMT(if (1) + STMT(if (1))); + if (1) + STMT(if (1)) 0; + if (1) + STMT(while (1)) 0; + if (1) + STMT(for (;;)) 0; + while (1) + STMT(if (1)) 0; + while (1) + STMT(while (1)) 0; + while (1) + STMT(for (;;)) 0; + for (;;) + STMT(if (1)) 0; + for (;;) + STMT(while (1)) 0; + for (;;) + STMT(for (;;)) 0; +} + +void STMT(fn2()) { +} + +void STMT(fn3)() { +} + // CHECK: foo // CHECK-NEXT: File 0, [[@LINE+1]]:17 -> {{[0-9]+}}:2 = #0 void foo(int i) { From 8a6e1d020e2759032b91aa2cadf21c3e9043e5d5 Mon Sep 17 00:00:00 2001 From: Matthias Braun Date: Sat, 30 Jan 2016 01:27:06 +0000 Subject: [PATCH 097/742] Avoid overly large SmallPtrSet/SmallSet These sets perform linear searching in small mode so it is never a good idea to use SmallSize/N bigger than 32. Differential Revision: http://reviews.llvm.org/D16705 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259284 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 63f7c930852ffe13b07ef4182fdd6e01f02538aa) --- lib/Analysis/PseudoConstantAnalysis.cpp | 4 +--- lib/Sema/SemaDeclObjC.cpp | 2 +- lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp | 2 +- lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp | 6 +----- 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/lib/Analysis/PseudoConstantAnalysis.cpp b/lib/Analysis/PseudoConstantAnalysis.cpp index 5b917a7a27f..614f676fb19 100644 --- a/lib/Analysis/PseudoConstantAnalysis.cpp +++ b/lib/Analysis/PseudoConstantAnalysis.cpp @@ -22,9 +22,7 @@ using namespace clang; -// The number of ValueDecls we want to keep track of by default (per-function) -#define VARDECL_SET_SIZE 256 -typedef llvm::SmallPtrSet VarDeclSet; +typedef llvm::SmallPtrSet VarDeclSet; PseudoConstantAnalysis::PseudoConstantAnalysis(const Stmt *DeclBody) : DeclBody(DeclBody), Analyzed(false) { diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index 3f957af489e..9ed1777a715 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -3854,7 +3854,7 @@ class OverrideSearch { } } - typedef llvm::SmallPtrSet::iterator iterator; + typedef llvm::SmallPtrSetImpl::iterator iterator; iterator begin() const { return Overridden.begin(); } iterator end() const { return Overridden.end(); } diff --git a/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp b/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp index a052d83f5af..64c30e7a82c 100644 --- a/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp @@ -43,7 +43,7 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G, ExprEngine &Eng) const { const CFG *C = nullptr; const SourceManager &SM = B.getSourceManager(); - llvm::SmallPtrSet reachable; + llvm::SmallPtrSet reachable; // Root node should have the location context of the top most function. const ExplodedNode *GraphRoot = *G.roots_begin(); diff --git a/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp b/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp index a03abce9626..892e713d241 100644 --- a/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp @@ -26,10 +26,6 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "llvm/ADT/SmallSet.h" -// The number of CFGBlock pointers we want to reserve memory for. This is used -// once for each function we analyze. -#define DEFAULT_CFGBLOCKS 256 - using namespace clang; using namespace ento; @@ -39,7 +35,7 @@ class UnreachableCodeChecker : public Checker { void checkEndAnalysis(ExplodedGraph &G, BugReporter &B, ExprEngine &Eng) const; private: - typedef llvm::SmallSet CFGBlocksSet; + typedef llvm::SmallSet CFGBlocksSet; static inline const Stmt *getUnreachableStmt(const CFGBlock *CB); static void FindUnreachableEntryPoints(const CFGBlock *CB, From 906e92cfb845fda14213c898e21468afc7882677 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Tue, 9 Feb 2016 19:07:07 +0000 Subject: [PATCH 098/742] [libclang] indexing: Have the semantic container of synthesized ObjC getter/setter methods be the implementation decl. Matches the behavior of other ObjC methods. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260250 91177308-0d34-0410-b5e6-96231b3b80d8 --- tools/libclang/IndexingContext.cpp | 11 +++++++---- tools/libclang/IndexingContext.h | 3 ++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/tools/libclang/IndexingContext.cpp b/tools/libclang/IndexingContext.cpp index f7640c63e05..5d944bae1cb 100644 --- a/tools/libclang/IndexingContext.cpp +++ b/tools/libclang/IndexingContext.cpp @@ -309,7 +309,8 @@ void IndexingContext::handleDiagnosticSet(CXDiagnostic CXDiagSet) { bool IndexingContext::handleDecl(const NamedDecl *D, SourceLocation Loc, CXCursor Cursor, DeclInfo &DInfo, - const DeclContext *LexicalDC) { + const DeclContext *LexicalDC, + const DeclContext *SemaDC) { if (!CB.indexDeclaration || !D) return false; if (D->isImplicit() && shouldIgnoreIfImplicit(D)) @@ -335,10 +336,12 @@ bool IndexingContext::handleDecl(const NamedDecl *D, DInfo.attributes = DInfo.EntInfo.attributes; DInfo.numAttributes = DInfo.EntInfo.numAttributes; - getContainerInfo(D->getDeclContext(), DInfo.SemanticContainer); + if (!SemaDC) + SemaDC = D->getDeclContext(); + getContainerInfo(SemaDC, DInfo.SemanticContainer); DInfo.semanticContainer = &DInfo.SemanticContainer; - if (LexicalDC == D->getDeclContext()) { + if (LexicalDC == SemaDC) { DInfo.lexicalContainer = &DInfo.SemanticContainer; } else if (isTemplateImplicitInstantiation(D)) { // Implicit instantiations have the lexical context of where they were @@ -598,7 +601,7 @@ bool IndexingContext::handleSynthesizedObjCMethod(const ObjCMethodDecl *D, const DeclContext *LexicalDC) { DeclInfo DInfo(/*isRedeclaration=*/true, /*isDefinition=*/true, /*isContainer=*/false); - return handleDecl(D, Loc, getCursor(D), DInfo, LexicalDC); + return handleDecl(D, Loc, getCursor(D), DInfo, LexicalDC, LexicalDC); } bool IndexingContext::handleObjCProperty(const ObjCPropertyDecl *D) { diff --git a/tools/libclang/IndexingContext.h b/tools/libclang/IndexingContext.h index 4da6aebaf64..2b1355ad32a 100644 --- a/tools/libclang/IndexingContext.h +++ b/tools/libclang/IndexingContext.h @@ -468,7 +468,8 @@ class IndexingContext { bool handleDecl(const NamedDecl *D, SourceLocation Loc, CXCursor Cursor, DeclInfo &DInfo, - const DeclContext *LexicalDC = nullptr); + const DeclContext *LexicalDC = nullptr, + const DeclContext *SemaDC = nullptr); bool handleObjCContainer(const ObjCContainerDecl *D, SourceLocation Loc, CXCursor Cursor, From 748d344ad01c675165848293261fcd27e3f0d508 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Tue, 9 Feb 2016 19:07:13 +0000 Subject: [PATCH 099/742] [ASTUnit] Change the parameter of ASTUnit::LoadFromCompilerInvocationAction to accept a more general FrontendAction. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260251 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Frontend/ASTUnit.h | 4 ++-- lib/Frontend/ASTUnit.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/clang/Frontend/ASTUnit.h b/include/clang/Frontend/ASTUnit.h index a5f7af57143..04e6dce5110 100644 --- a/include/clang/Frontend/ASTUnit.h +++ b/include/clang/Frontend/ASTUnit.h @@ -60,7 +60,7 @@ class PCHContainerOperations; class PCHContainerReader; class SourceManager; class TargetInfo; -class ASTFrontendAction; +class FrontendAction; class ASTDeserializationListener; /// \brief Utility class for loading a ASTContext from an AST file. @@ -781,7 +781,7 @@ class ASTUnit : public ModuleLoader { CompilerInvocation *CI, std::shared_ptr PCHContainerOps, IntrusiveRefCntPtr Diags, - ASTFrontendAction *Action = nullptr, ASTUnit *Unit = nullptr, + FrontendAction *Action = nullptr, ASTUnit *Unit = nullptr, bool Persistent = true, StringRef ResourceFilesPath = StringRef(), bool OnlyLocalDecls = false, bool CaptureDiagnostics = false, unsigned PrecompilePreambleAfterNParses = 0, diff --git a/lib/Frontend/ASTUnit.cpp b/lib/Frontend/ASTUnit.cpp index e6ba29201f8..54ac6048bc1 100644 --- a/lib/Frontend/ASTUnit.cpp +++ b/lib/Frontend/ASTUnit.cpp @@ -1723,7 +1723,7 @@ ASTUnit *ASTUnit::create(CompilerInvocation *CI, ASTUnit *ASTUnit::LoadFromCompilerInvocationAction( CompilerInvocation *CI, std::shared_ptr PCHContainerOps, - IntrusiveRefCntPtr Diags, ASTFrontendAction *Action, + IntrusiveRefCntPtr Diags, FrontendAction *Action, ASTUnit *Unit, bool Persistent, StringRef ResourceFilesPath, bool OnlyLocalDecls, bool CaptureDiagnostics, unsigned PrecompilePreambleAfterNParses, bool CacheCodeCompletionResults, @@ -1812,7 +1812,7 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocationAction( // Create the source manager. Clang->setSourceManager(&AST->getSourceManager()); - ASTFrontendAction *Act = Action; + FrontendAction *Act = Action; std::unique_ptr TrackerAct; if (!Act) { From 51d44c3aa24d2475ad2b5a4d7b631184c058ab79 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Tue, 9 Feb 2016 19:07:16 +0000 Subject: [PATCH 100/742] [Frontend] Handle ASTConsumer::shouldSkipFunctionBody via the MultiplexConsumer. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260252 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Frontend/MultiplexConsumer.h | 1 + lib/Frontend/MultiplexConsumer.cpp | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/include/clang/Frontend/MultiplexConsumer.h b/include/clang/Frontend/MultiplexConsumer.h index 97828293387..ae6db29258d 100644 --- a/include/clang/Frontend/MultiplexConsumer.h +++ b/include/clang/Frontend/MultiplexConsumer.h @@ -53,6 +53,7 @@ class MultiplexConsumer : public SemaConsumer { ASTMutationListener *GetASTMutationListener() override; ASTDeserializationListener *GetASTDeserializationListener() override; void PrintStats() override; + bool shouldSkipFunctionBody(Decl *D) override; // SemaConsumer void InitializeSema(Sema &S) override; diff --git a/lib/Frontend/MultiplexConsumer.cpp b/lib/Frontend/MultiplexConsumer.cpp index fdeb04f6248..d1931feeb35 100644 --- a/lib/Frontend/MultiplexConsumer.cpp +++ b/lib/Frontend/MultiplexConsumer.cpp @@ -355,6 +355,13 @@ void MultiplexConsumer::PrintStats() { Consumer->PrintStats(); } +bool MultiplexConsumer::shouldSkipFunctionBody(Decl *D) { + bool Skip = true; + for (auto &Consumer : Consumers) + Skip = Skip && Consumer->shouldSkipFunctionBody(D); + return Skip; +} + void MultiplexConsumer::InitializeSema(Sema &S) { for (auto &Consumer : Consumers) if (SemaConsumer *SC = dyn_cast(Consumer.get())) From c6a402bfbb77557a788141fd9cd3226a0c17258b Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Tue, 9 Feb 2016 19:07:19 +0000 Subject: [PATCH 101/742] [libclang] indexing: for a synthesized property reference have the parent be the ObjC implementation decl. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260253 91177308-0d34-0410-b5e6-96231b3b80d8 --- tools/libclang/IndexingContext.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/libclang/IndexingContext.cpp b/tools/libclang/IndexingContext.cpp index 5d944bae1cb..7f65412f989 100644 --- a/tools/libclang/IndexingContext.cpp +++ b/tools/libclang/IndexingContext.cpp @@ -592,8 +592,9 @@ bool IndexingContext::handleObjCMethod(const ObjCMethodDecl *D) { bool IndexingContext::handleSynthesizedObjCProperty( const ObjCPropertyImplDecl *D) { ObjCPropertyDecl *PD = D->getPropertyDecl(); - return handleReference(PD, D->getLocation(), getCursor(D), nullptr, - D->getDeclContext()); + auto *DC = D->getDeclContext(); + return handleReference(PD, D->getLocation(), getCursor(D), + dyn_cast(DC), DC); } bool IndexingContext::handleSynthesizedObjCMethod(const ObjCMethodDecl *D, From 7386416ccaa4ccae9fde7bc752599110afcdc90b Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Tue, 9 Feb 2016 19:07:21 +0000 Subject: [PATCH 102/742] [libclang] indexing: handle 'TopLevelDeclInObjCContainers' at the point where they are reported. It isn't much benefit and doesn't worth the complexity to try to handle them after the container is encountered. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260254 91177308-0d34-0410-b5e6-96231b3b80d8 --- tools/libclang/IndexDecl.cpp | 16 ---------------- tools/libclang/Indexing.cpp | 5 ++--- tools/libclang/IndexingContext.h | 7 ------- 3 files changed, 2 insertions(+), 26 deletions(-) diff --git a/tools/libclang/IndexDecl.cpp b/tools/libclang/IndexDecl.cpp index c8cf1d36214..aa97129c23f 100644 --- a/tools/libclang/IndexDecl.cpp +++ b/tools/libclang/IndexDecl.cpp @@ -136,7 +136,6 @@ class IndexingDeclVisitor : public ConstDeclVisitor { IndexCtx.handleObjCInterface(D); if (D->isThisDeclarationADefinition()) { - IndexCtx.indexTUDeclsInObjCContainer(); IndexCtx.indexDeclContext(D); } return true; @@ -146,7 +145,6 @@ class IndexingDeclVisitor : public ConstDeclVisitor { IndexCtx.handleObjCProtocol(D); if (D->isThisDeclarationADefinition()) { - IndexCtx.indexTUDeclsInObjCContainer(); IndexCtx.indexDeclContext(D); } return true; @@ -162,8 +160,6 @@ class IndexingDeclVisitor : public ConstDeclVisitor { IndexCtx.handleObjCImplementation(D); - IndexCtx.indexTUDeclsInObjCContainer(); - // Index the ivars first to make sure the synthesized ivars are indexed // before indexing the methods that can reference them. for (const auto *IvarI : D->ivars()) @@ -178,8 +174,6 @@ class IndexingDeclVisitor : public ConstDeclVisitor { bool VisitObjCCategoryDecl(const ObjCCategoryDecl *D) { IndexCtx.handleObjCCategory(D); - - IndexCtx.indexTUDeclsInObjCContainer(); IndexCtx.indexDeclContext(D); return true; } @@ -190,8 +184,6 @@ class IndexingDeclVisitor : public ConstDeclVisitor { return true; IndexCtx.handleObjCCategoryImpl(D); - - IndexCtx.indexTUDeclsInObjCContainer(); IndexCtx.indexDeclContext(D); return true; } @@ -347,11 +339,3 @@ void IndexingContext::indexDeclGroupRef(DeclGroupRef DG) { for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) indexTopLevelDecl(*I); } - -void IndexingContext::indexTUDeclsInObjCContainer() { - while (!TUDeclsInObjCContainer.empty()) { - DeclGroupRef DG = TUDeclsInObjCContainer.front(); - TUDeclsInObjCContainer.pop_front(); - indexDeclGroupRef(DG); - } -} diff --git a/tools/libclang/Indexing.cpp b/tools/libclang/Indexing.cpp index d6e35b0019c..4929d6244e5 100644 --- a/tools/libclang/Indexing.cpp +++ b/tools/libclang/Indexing.cpp @@ -327,9 +327,8 @@ class IndexingConsumer : public ASTConsumer { /// \brief Handle the specified top-level declaration that occurred inside /// and ObjC container. - void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override { - // They will be handled after the interface is seen first. - IndexCtx.addTUDeclInObjCContainer(D); + void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) override { + IndexCtx.indexDeclGroupRef(DG); } /// \brief This is called by the AST reader when deserializing things. diff --git a/tools/libclang/IndexingContext.h b/tools/libclang/IndexingContext.h index 2b1355ad32a..d1d62c90d45 100644 --- a/tools/libclang/IndexingContext.h +++ b/tools/libclang/IndexingContext.h @@ -292,8 +292,6 @@ class IndexingContext { typedef std::pair RefFileOccurrence; llvm::DenseSet RefFileOccurrences; - std::deque TUDeclsInObjCContainer; - llvm::BumpPtrAllocator StrScratch; unsigned StrAdapterCount; friend class ScratchAlloc; @@ -446,13 +444,8 @@ class IndexingContext { bool isNotFromSourceFile(SourceLocation Loc) const; void indexTopLevelDecl(const Decl *D); - void indexTUDeclsInObjCContainer(); void indexDeclGroupRef(DeclGroupRef DG); - void addTUDeclInObjCContainer(DeclGroupRef DG) { - TUDeclsInObjCContainer.push_back(DG); - } - void translateLoc(SourceLocation Loc, CXIdxClientFile *indexFile, CXFile *file, unsigned *line, unsigned *column, unsigned *offset); From 57e950dd85eae2ac92b5f51a87a0be19e15d63cb Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Tue, 9 Feb 2016 19:07:24 +0000 Subject: [PATCH 103/742] [libclang] indexing: make sure to not visit init-list expressions twice. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260255 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/Index/index-refs.cpp | 8 +++++ tools/libclang/IndexBody.cpp | 69 ++++++++++++++++++++++++++++++------ 2 files changed, 66 insertions(+), 11 deletions(-) diff --git a/test/Index/index-refs.cpp b/test/Index/index-refs.cpp index adbf02a7c6e..5a1ba1e8b43 100644 --- a/test/Index/index-refs.cpp +++ b/test/Index/index-refs.cpp @@ -69,6 +69,8 @@ void foo5() { struct S2 s = { .y = 1, .x = 4}; } +int ginitlist[] = {EnumVal}; + // RUN: c-index-test -index-file %s | FileCheck %s // CHECK: [indexDeclaration]: kind: namespace | name: NS // CHECK-NEXT: [indexDeclaration]: kind: variable | name: gx @@ -119,3 +121,9 @@ void foo5() { // CHECK: [indexEntityReference]: kind: field | name: y | {{.*}} | loc: 69:20 // CHECK-NEXT: [indexEntityReference]: kind: field | name: x | {{.*}} | loc: 69:28 +// CHECK-NOT: [indexEntityReference]: kind: field | name: y | {{.*}} | loc: 69:20 +// CHECK-NOT: [indexEntityReference]: kind: field | name: x | {{.*}} | loc: 69:28 + +// CHECK: [indexDeclaration]: kind: variable | name: ginitlist | +// CHECK: [indexEntityReference]: kind: enumerator | name: EnumVal | {{.*}} | loc: 72:20 +// CHECK-NOT: [indexEntityReference]: kind: enumerator | name: EnumVal | {{.*}} | loc: 72:20 diff --git a/tools/libclang/IndexBody.cpp b/tools/libclang/IndexBody.cpp index 64df4b85bea..58dc11722bf 100644 --- a/tools/libclang/IndexBody.cpp +++ b/tools/libclang/IndexBody.cpp @@ -50,17 +50,6 @@ class BodyIndexer : public RecursiveASTVisitor { return true; } - bool VisitDesignatedInitExpr(DesignatedInitExpr *E) { - for (DesignatedInitExpr::reverse_designators_iterator - D = E->designators_rbegin(), DEnd = E->designators_rend(); - D != DEnd; ++D) { - if (D->isFieldDesignator()) - IndexCtx.handleReference(D->getField(), D->getFieldLoc(), - Parent, ParentDC, E); - } - return true; - } - bool VisitObjCIvarRefExpr(ObjCIvarRefExpr *E) { IndexCtx.handleReference(E->getDecl(), E->getLocation(), Parent, ParentDC, E); @@ -162,6 +151,64 @@ class BodyIndexer : public RecursiveASTVisitor { return true; } + // RecursiveASTVisitor visits both syntactic and semantic forms, duplicating + // the things that we visit. Make sure to only visit the semantic form. + // Also visit things that are in the syntactic form but not the semantic one, + // for example the indices in DesignatedInitExprs. + bool TraverseInitListExpr(InitListExpr *S) { + + class SyntacticFormIndexer : + public RecursiveASTVisitor { + IndexingContext &IndexCtx; + const NamedDecl *Parent; + const DeclContext *ParentDC; + + public: + SyntacticFormIndexer(IndexingContext &indexCtx, + const NamedDecl *Parent, const DeclContext *DC) + : IndexCtx(indexCtx), Parent(Parent), ParentDC(DC) { } + + bool shouldWalkTypesOfTypeLocs() const { return false; } + + bool VisitDesignatedInitExpr(DesignatedInitExpr *E) { + for (DesignatedInitExpr::reverse_designators_iterator + D = E->designators_rbegin(), DEnd = E->designators_rend(); + D != DEnd; ++D) { + if (D->isFieldDesignator()) + IndexCtx.handleReference(D->getField(), D->getFieldLoc(), + Parent, ParentDC, E); + } + return true; + } + }; + + auto visitForm = [&](InitListExpr *Form) { + for (Stmt *SubStmt : Form->children()) { + if (!TraverseStmt(SubStmt)) + return false; + } + return true; + }; + + InitListExpr *SemaForm = S->isSemanticForm() ? S : S->getSemanticForm(); + InitListExpr *SyntaxForm = S->isSemanticForm() ? S->getSyntacticForm() : S; + + if (SemaForm) { + // Visit things present in syntactic form but not the semantic form. + if (SyntaxForm) { + SyntacticFormIndexer(IndexCtx, Parent, ParentDC).TraverseStmt(SyntaxForm); + } + return visitForm(SemaForm); + } + + // No semantic, try the syntactic. + if (SyntaxForm) { + return visitForm(SyntaxForm); + } + + return true; + } + }; } // anonymous namespace From dc7d37753771bcc649b9d8a0040f5ffe7dfe7ffa Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Fri, 29 Jan 2016 15:53:43 -0800 Subject: [PATCH 104/742] Introduce ns_error_domain attribute. ns_error_domain can be used by, e.g. NS_ERROR_ENUM, in order to identify a global declaration representing the domain constant. Introduces the attribute, Sema handling, diagnostics, and test case. --- include/clang/Basic/Attr.td | 6 ++++ include/clang/Basic/AttrDocs.td | 7 ++++ include/clang/Basic/DiagnosticSemaKinds.td | 7 ++++ lib/Sema/SemaDeclAttr.cpp | 37 +++++++++++++++++++ test/Analysis/ns_error_enum.m | 42 ++++++++++++++++++++++ 5 files changed, 99 insertions(+) create mode 100644 test/Analysis/ns_error_enum.m diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index a7e073d5cae..c6126866fed 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -1115,6 +1115,12 @@ def ObjCBridgeRelated : InheritableAttr { let Documentation = [Undocumented]; } +def NSErrorDomain : Attr { + let Spellings = [GNU<"ns_error_domain">]; + let Args = [IdentifierArgument<"ErrorDomain">]; + let Documentation = [NSErrorDomainDocs]; +} + def NSReturnsRetained : InheritableAttr { let Spellings = [GNU<"ns_returns_retained">]; // let Subjects = SubjectList<[ObjCMethod, ObjCProperty, Function]>; diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td index 5c4473d08d9..08cd99b9867 100644 --- a/include/clang/Basic/AttrDocs.td +++ b/include/clang/Basic/AttrDocs.td @@ -1758,6 +1758,13 @@ arguments, with arbitrary offsets. }]; } +def NSErrorDomainDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``ns_error_domain`` attribute indicates a global constant representing the error domain. + }]; +} + def SwiftDocs : DocumentationCategory<"Controlling Swift Import"> { let Content = [{ Clang supports additional attributes for controlling how APIs are imported into Swift. diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 33d2a0e98c3..dd9fb576255 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -7514,6 +7514,13 @@ def err_nsconsumed_attribute_mismatch : Error< def err_nsreturns_retained_attribute_mismatch : Error< "overriding method has mismatched ns_returns_%select{not_retained|retained}0" " attributes">; + +def err_nserrordomain_not_tagdecl : Error< + "ns_error_domain attribute only valid on enum/struct/union/class">; +def err_nserrordomain_invalid_decl : Error< + "domain argument %0 not valid top-level declaration">; +def err_nserrordomain_requires_identifier : Error< + "domain argument must be an identifier">; def note_getter_unavailable : Note< "or because setter is declared here, but no getter method %0 is found">; diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index c1c4d1e75a5..d58fdc7c13f 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -4214,6 +4214,39 @@ static void handleObjCRequiresSuperAttr(Sema &S, Decl *D, attr.getAttributeSpellingListIndex())); } +static void handleNSErrorDomain(Sema &S, Decl *D, const AttributeList &Attr) { + if (!isa(D)) { + S.Diag(D->getLocStart(), diag::err_nserrordomain_not_tagdecl); + return; + } + IdentifierLoc *identLoc = + Attr.isArgIdent(0) ? Attr.getArgAsIdent(0) : nullptr; + if (!identLoc || !identLoc->Ident) { + // Try to locate the argument directly + SourceLocation loc = Attr.getLoc(); + if (Attr.isArgExpr(0) && Attr.getArgAsExpr(0)) + loc = Attr.getArgAsExpr(0)->getLocStart(); + + S.Diag(loc, diag::err_nserrordomain_requires_identifier); + return; + } + + // Verify that the identifier is a valid decl in the C decl namespace + LookupResult lookupResult(S, DeclarationName(identLoc->Ident), + SourceLocation(), + Sema::LookupNameKind::LookupOrdinaryName); + if (!S.LookupName(lookupResult, S.TUScope) || + !lookupResult.getAsSingle()) { + S.Diag(identLoc->Loc, diag::err_nserrordomain_invalid_decl) + << identLoc->Ident; + return; + } + + D->addAttr(::new (S.Context) + NSErrorDomainAttr(Attr.getRange(), S.Context, identLoc->Ident, + Attr.getAttributeSpellingListIndex())); +} + static void handleCFAuditedTransferAttr(Sema &S, Decl *D, const AttributeList &Attr) { if (checkAttrMutualExclusion(S, D, Attr.getRange(), @@ -5509,6 +5542,10 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_ObjCBoxable: handleObjCBoxable(S, D, Attr); break; + + case AttributeList::AT_NSErrorDomain: + handleNSErrorDomain(S, D, Attr); + break; case AttributeList::AT_CFAuditedTransfer: handleCFAuditedTransferAttr(S, D, Attr); diff --git a/test/Analysis/ns_error_enum.m b/test/Analysis/ns_error_enum.m new file mode 100644 index 00000000000..79e3c25cbaa --- /dev/null +++ b/test/Analysis/ns_error_enum.m @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -verify %s + +#define CF_ENUM(_type, _name) enum _name : _type _name; enum _name : _type +#define NS_ENUM(_type, _name) CF_ENUM(_type, _name) + +#define NS_ERROR_ENUM(_type, _name, _domain) \ + enum _name : _type _name; enum __attribute__((ns_error_domain(_domain))) _name : _type + +typedef NS_ENUM(unsigned, MyEnum) { + MyFirst, + MySecond, +}; + +typedef NS_ENUM(invalidType, MyInvalidEnum) { +// expected-error@-1{{unknown type name 'invalidType'}} +// expected-error@-2{{unknown type name 'invalidType'}} + MyFirstInvalid, + MySecondInvalid, +}; + +const char *MyErrorDomain; +typedef NS_ERROR_ENUM(unsigned char, MyErrorEnum, MyErrorDomain) { + MyErrFirst, + MyErrSecond, +}; +struct __attribute__((ns_error_domain(MyErrorDomain))) MyStructErrorDomain {}; + +typedef NS_ERROR_ENUM(unsigned char, MyErrorEnumInvalid, InvalidDomain) { + // expected-error@-1{{domain argument 'InvalidDomain' not valid top-level declaration}} + MyErrFirstInvalid, + MyErrSecondInvalid, +}; + +typedef NS_ERROR_ENUM(unsigned char, MyErrorEnumInvalid, "domain-string"); + // expected-error@-1{{domain argument must be an identifier}} + +int __attribute__((ns_error_domain(MyErrorDomain))) NotTagDecl; + // expected-error@-1{{ns_error_domain attribute only valid on enum/struct/union/class}} + +void foo() {} +typedef NS_ERROR_ENUM(unsigned char, MyErrorEnumInvalidFunction, foo); + // expected-error@-1{{domain argument 'foo' not valid top-level declaration}} From a7c8a1176c2ca1befe5d0fe165eb657b49e83422 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Wed, 10 Feb 2016 06:06:06 +0000 Subject: [PATCH 105/742] [Sema] Issue a warning for integer overflow in nested struct initializer r257357 fixed clang to warn on integer overflow in struct initializers. However, it didn't warn when a struct had a nested initializer. This commit makes changes in Sema::CheckForIntOverflow to handle nested initializers. For example: struct s { struct t { unsigned x; } t; } s = { { .x = 4 * 1024 * 1024 * 1024 } }; rdar://problem/23526454 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260360 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 3d46479781659e5844a87d0c84952af392aada3a) --- lib/Sema/SemaChecking.cpp | 20 ++++++++++++++------ test/Sema/integer-overflow.c | 20 ++++++++++++++++++++ 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index 6c2834b750a..4c3dd487f19 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -7852,12 +7852,20 @@ void Sema::CheckBoolLikeConversion(Expr *E, SourceLocation CC) { /// Diagnose when expression is an integer constant expression and its evaluation /// results in integer overflow void Sema::CheckForIntOverflow (Expr *E) { - if (isa(E->IgnoreParenCasts())) - E->IgnoreParenCasts()->EvaluateForOverflow(Context); - else if (auto InitList = dyn_cast(E)) - for (Expr *E : InitList->inits()) - if (isa(E->IgnoreParenCasts())) - E->IgnoreParenCasts()->EvaluateForOverflow(Context); + // Use a work list to deal with nested struct initializers. + SmallVector Exprs(1, E); + + do { + Expr *E = Exprs.pop_back_val(); + + if (isa(E->IgnoreParenCasts())) { + E->IgnoreParenCasts()->EvaluateForOverflow(Context); + continue; + } + + if (auto InitList = dyn_cast(E)) + Exprs.append(InitList->inits().begin(), InitList->inits().end()); + } while (!Exprs.empty()); } namespace { diff --git a/test/Sema/integer-overflow.c b/test/Sema/integer-overflow.c index db5c1f4c711..02d99b3fc5c 100644 --- a/test/Sema/integer-overflow.c +++ b/test/Sema/integer-overflow.c @@ -153,3 +153,23 @@ struct s { .y = 5, .x = 4 * 1024 * 1024 * 1024 // expected-warning {{overflow in expression; result is 0 with type 'int'}} }; + +struct s2 { + unsigned a0; + + struct s3 { + unsigned a2; + + struct s4 { + unsigned a4; + } a3; + } a1; +} s2 = { + .a0 = 4 * 1024 * 1024 * 1024, // expected-warning {{overflow in expression; result is 0 with type 'int'}} + { + .a2 = 4 * 1024 * 1024 * 1024, // expected-warning {{overflow in expression; result is 0 with type 'int'}} + { + .a4 = 4 * 1024 * 1024 * 1024 // expected-warning {{overflow in expression; result is 0 with type 'int'}} + } + } +}; From bcba6997f0b26a831e7720f6ea26663c0d861558 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Thu, 11 Feb 2016 06:36:35 +0000 Subject: [PATCH 106/742] [Objective-c] Stop attaching section "datacoal_nt" to global variables. The current macho linker just copies symbols in section datacoal_nt to section data, so it doesn't really matter whether or not section "datacoal_nt" is attached to the global variable. This is a follow-up to r250370, which made changes in llvm to stop putting functions and data in the *coal* sections. rdar://problem/24528611 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260496 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 2f720ab5abde0127a976c495b1fc63d240bc88a3) --- lib/CodeGen/CGObjCMac.cpp | 7 +------ lib/Frontend/Rewrite/RewriteModernObjC.cpp | 2 +- test/CodeGenObjC/exceptions-asm-attribute.m | 6 +++--- test/CodeGenObjC/metadata-symbols-64.m | 2 +- test/CodeGenObjC/metadata_symbols.m | 6 +++--- 5 files changed, 9 insertions(+), 14 deletions(-) diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index fdbc983aa5b..b61d057f350 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/lib/CodeGen/CGObjCMac.cpp @@ -6426,7 +6426,7 @@ llvm::Constant *CGObjCNonFragileABIMac::GetOrEmitProtocolRef( const ObjCProtocolDecl *PD) { llvm::GlobalVariable *&Entry = Protocols[PD->getIdentifier()]; - if (!Entry) { + if (!Entry) // We use the initializer as a marker of whether this is a forward // reference or not. At module finalization we add the empty // contents for protocols which were referenced but never defined. @@ -6435,8 +6435,6 @@ llvm::Constant *CGObjCNonFragileABIMac::GetOrEmitProtocolRef( false, llvm::GlobalValue::ExternalLinkage, nullptr, "\01l_OBJC_PROTOCOL_$_" + PD->getObjCRuntimeNameAsString()); - Entry->setSection("__DATA,__datacoal_nt,coalesced"); - } return Entry; } @@ -6563,7 +6561,6 @@ llvm::Constant *CGObjCNonFragileABIMac::GetOrEmitProtocol( "\01l_OBJC_PROTOCOL_$_" + PD->getObjCRuntimeNameAsString()); Entry->setAlignment( CGM.getDataLayout().getABITypeAlignment(ObjCTypes.ProtocolnfABITy)); - Entry->setSection("__DATA,__datacoal_nt,coalesced"); Protocols[PD->getIdentifier()] = Entry; } @@ -7306,8 +7303,6 @@ CGObjCNonFragileABIMac::GetInterfaceEHType(const ObjCInterfaceDecl *ID, if (ForDefinition) Entry->setSection("__DATA,__objc_const"); - else - Entry->setSection("__DATA,__datacoal_nt,coalesced"); return Entry; } diff --git a/lib/Frontend/Rewrite/RewriteModernObjC.cpp b/lib/Frontend/Rewrite/RewriteModernObjC.cpp index 9fae7f02995..4d57330ee0e 100644 --- a/lib/Frontend/Rewrite/RewriteModernObjC.cpp +++ b/lib/Frontend/Rewrite/RewriteModernObjC.cpp @@ -7008,7 +7008,7 @@ void RewriteModernObjC::RewriteObjCProtocolMetaData(ObjCProtocolDecl *PDecl, Result += "static "; Result += "struct _protocol_t _OBJC_PROTOCOL_"; Result += PDecl->getNameAsString(); - Result += " __attribute__ ((used, section (\"__DATA,__datacoal_nt,coalesced\"))) = {\n"; + Result += " __attribute__ ((used)) = {\n"; Result += "\t0,\n"; // id is; is null Result += "\t\""; Result += PDecl->getNameAsString(); Result += "\",\n"; if (SuperProtocols.size() > 0) { diff --git a/test/CodeGenObjC/exceptions-asm-attribute.m b/test/CodeGenObjC/exceptions-asm-attribute.m index c5ef46724b5..efca3cee171 100644 --- a/test/CodeGenObjC/exceptions-asm-attribute.m +++ b/test/CodeGenObjC/exceptions-asm-attribute.m @@ -12,7 +12,7 @@ // CHECK-X86_64: @"OBJC_CLASS_$_MySecretNamespace.A" = global {{.*}}, section "__DATA, __objc_data", align 8 // CHECK-X86_64: @"OBJC_METACLASS_$_MySecretNamespace.A" = global {{.*}}, section "__DATA, __objc_data", align 8 // CHECK-X86_64: @OBJC_CLASS_NAME_ = {{.*}}, section "__TEXT,__objc_classname,cstring_literals", align 1 -// CHECK-X86_64: @"OBJC_EHTYPE_$_MySecretNamespace.EH1" = weak global {{.*}}, section "__DATA,__datacoal_nt,coalesced", align 8 +// CHECK-X86_64: @"OBJC_EHTYPE_$_MySecretNamespace.EH1" = weak global {{.*}}, align 8 // CHECK-X86_64: @"OBJC_EHTYPE_$_MySecretNamespace.EH2" = external global // CHECK-X86_64: @"OBJC_EHTYPE_$_MySecretNamespace.EH3" = global {{.*}}, section "__DATA,__objc_const", align 8 // CHECK-X86_64: @"OBJC_LABEL_CLASS_$" = private global {{.*}}, section "__DATA, __objc_classlist, regular, no_dead_strip", align 8 @@ -24,7 +24,7 @@ // CHECK-X86_64-HIDDEN: @"OBJC_CLASS_$_MySecretNamespace.A" = hidden global {{.*}}, section "__DATA, __objc_data", align 8 // CHECK-X86_64-HIDDEN: @"OBJC_METACLASS_$_MySecretNamespace.A" = hidden global {{.*}}, section "__DATA, __objc_data", align 8 -// CHECK-X86_64-HIDDEN: @"OBJC_EHTYPE_$_MySecretNamespace.EH1" = weak hidden global {{.*}}, section "__DATA,__datacoal_nt,coalesced" +// CHECK-X86_64-HIDDEN: @"OBJC_EHTYPE_$_MySecretNamespace.EH1" = weak hidden global {{.*}} // CHECK-X86_64-HIDDEN: @"OBJC_EHTYPE_$_MySecretNamespace.EH2" = external global // CHECK-X86_64-HIDDEN: @"OBJC_EHTYPE_$_MySecretNamespace.EH3" = hidden global {{.*}}, section "__DATA,__objc_const", align 8 // CHECK-X86_64-HIDDEN: define internal void @"\01-[A im0]" @@ -36,7 +36,7 @@ // CHECK-ARMV6: @"OBJC_CLASS_$_MySecretNamespace.A" = global {{.*}}, section "__DATA, __objc_data", align 4 // CHECK-ARMV6: @"OBJC_METACLASS_$_MySecretNamespace.A" = global {{.*}}, section "__DATA, __objc_data", align 4 // CHECK-ARMV6: @OBJC_CLASS_NAME_ = {{.*}}, section "__TEXT,__objc_classname,cstring_literals", align 1 -// CHECK-ARMV6: @"OBJC_EHTYPE_$_MySecretNamespace.EH1" = weak global {{.*}}, section "__DATA,__datacoal_nt,coalesced", align 4 +// CHECK-ARMV6: @"OBJC_EHTYPE_$_MySecretNamespace.EH1" = weak global {{.*}}, align 4 // CHECK-ARMV6: @"OBJC_EHTYPE_$_MySecretNamespace.EH2" = external global // CHECK-ARMV6: @"OBJC_EHTYPE_$_MySecretNamespace.EH3" = global {{.*}}, section "__DATA,__objc_const", align 4 // CHECK-ARMV6: @"OBJC_LABEL_CLASS_$" = private global {{.*}}, section "__DATA, __objc_classlist, regular, no_dead_strip", align 4 diff --git a/test/CodeGenObjC/metadata-symbols-64.m b/test/CodeGenObjC/metadata-symbols-64.m index 5b9591ff973..9b742374638 100644 --- a/test/CodeGenObjC/metadata-symbols-64.m +++ b/test/CodeGenObjC/metadata-symbols-64.m @@ -11,7 +11,7 @@ // CHECK: @"\01l_OBJC_$_CLASS_METHODS_A" = private global {{.*}} section "__DATA, __objc_const", align 8 // CHECK: @"\01l_OBJC_$_PROTOCOL_INSTANCE_METHODS_P" = private global {{.*}} section "__DATA, __objc_const", align 8 // CHECK: @"\01l_OBJC_$_PROTOCOL_CLASS_METHODS_P" = private global {{.*}} section "__DATA, __objc_const", align 8 -// CHECK: @"\01l_OBJC_PROTOCOL_$_P" = weak hidden global {{.*}} section "__DATA,__datacoal_nt,coalesced", align 8 +// CHECK: @"\01l_OBJC_PROTOCOL_$_P" = weak hidden global {{.*}}, align 8 // CHECK: @"\01l_OBJC_LABEL_PROTOCOL_$_P" = weak hidden global {{.*}} section "__DATA, __objc_protolist, coalesced, no_dead_strip", align 8 // CHECK: @"\01l_OBJC_CLASS_PROTOCOLS_$_A" = private global {{.*}} section "__DATA, __objc_const", align 8 // CHECK: @"\01l_OBJC_METACLASS_RO_$_A" = private global {{.*}} section "__DATA, __objc_const", align 8 diff --git a/test/CodeGenObjC/metadata_symbols.m b/test/CodeGenObjC/metadata_symbols.m index 2c44fb59bd2..3e8f11f74f3 100644 --- a/test/CodeGenObjC/metadata_symbols.m +++ b/test/CodeGenObjC/metadata_symbols.m @@ -11,7 +11,7 @@ // CHECK-X86_64: @"OBJC_CLASS_$_A" = global {{.*}}, section "__DATA, __objc_data", align 8 // CHECK-X86_64: @"OBJC_METACLASS_$_A" = global {{.*}}, section "__DATA, __objc_data", align 8 // CHECK-X86_64: @OBJC_CLASS_NAME_ = {{.*}}, section "__TEXT,__objc_classname,cstring_literals", align 1 -// CHECK-X86_64: @"OBJC_EHTYPE_$_EH1" = weak global {{.*}}, section "__DATA,__datacoal_nt,coalesced", align 8 +// CHECK-X86_64: @"OBJC_EHTYPE_$_EH1" = weak global {{.*}}, align 8 // CHECK-X86_64: @"OBJC_EHTYPE_$_EH2" = external global // CHECK-X86_64: @"OBJC_EHTYPE_$_EH3" = global {{.*}}, section "__DATA,__objc_const", align 8 // CHECK-X86_64: @"OBJC_LABEL_CLASS_$" = private global {{.*}}, section "__DATA, __objc_classlist, regular, no_dead_strip", align 8 @@ -23,7 +23,7 @@ // CHECK-X86_64-HIDDEN: @"OBJC_CLASS_$_A" = hidden global {{.*}}, section "__DATA, __objc_data", align 8 // CHECK-X86_64-HIDDEN: @"OBJC_METACLASS_$_A" = hidden global {{.*}}, section "__DATA, __objc_data", align 8 -// CHECK-X86_64-HIDDEN: @"OBJC_EHTYPE_$_EH1" = weak hidden global {{.*}}, section "__DATA,__datacoal_nt,coalesced" +// CHECK-X86_64-HIDDEN: @"OBJC_EHTYPE_$_EH1" = weak hidden global {{.*}} // CHECK-X86_64-HIDDEN: @"OBJC_EHTYPE_$_EH2" = external global // CHECK-X86_64-HIDDEN: @"OBJC_EHTYPE_$_EH3" = hidden global {{.*}}, section "__DATA,__objc_const", align 8 // CHECK-X86_64-HIDDEN: define internal void @"\01-[A im0]" @@ -35,7 +35,7 @@ // CHECK-ARMV6: @"OBJC_CLASS_$_A" = global {{.*}}, section "__DATA, __objc_data", align 4 // CHECK-ARMV6: @"OBJC_METACLASS_$_A" = global {{.*}}, section "__DATA, __objc_data", align 4 // CHECK-ARMV6: @OBJC_CLASS_NAME_ = {{.*}}, section "__TEXT,__objc_classname,cstring_literals", align 1 -// CHECK-ARMV6: @"OBJC_EHTYPE_$_EH1" = weak global {{.*}}, section "__DATA,__datacoal_nt,coalesced", align 4 +// CHECK-ARMV6: @"OBJC_EHTYPE_$_EH1" = weak global {{.*}}, align 4 // CHECK-ARMV6: @"OBJC_EHTYPE_$_EH2" = external global // CHECK-ARMV6: @"OBJC_EHTYPE_$_EH3" = global {{.*}}, section "__DATA,__objc_const", align 4 // CHECK-ARMV6: @"OBJC_LABEL_CLASS_$" = private global {{.*}}, section "__DATA, __objc_classlist, regular, no_dead_strip", align 4 From a168c823c5425c002327f3c3d4c00f85023c2683 Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Thu, 11 Feb 2016 17:04:42 +0000 Subject: [PATCH 107/742] [Modules] Don't infinite recurse on implicit import of circular modules in preamble Update the Preprocessor's VisibleModuleSet when typo-correction creates an implicit module import so that we won't accidentally write an invalid SourceLocation into the preamble AST. This would later lead to infinite recursion when loading the preamble AST because we use the value in ImportLocs to prevent visiting a module twice. rdar://problem/24440990 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260543 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Basic/Module.cpp | 1 + lib/Serialization/ASTReader.cpp | 4 +++- test/Index/Inputs/module.map | 14 ++++++++++++++ .../Index/Inputs/preamble-with-implicit-import-A.h | 1 + .../Index/Inputs/preamble-with-implicit-import-B.h | 3 +++ .../Index/Inputs/preamble-with-implicit-import-C.h | 2 ++ test/Index/Inputs/preamble-with-implicit-import.h | 4 ++++ test/Index/preamble-with-implicit-import.m | 6 ++++++ 8 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 test/Index/Inputs/preamble-with-implicit-import-A.h create mode 100644 test/Index/Inputs/preamble-with-implicit-import-B.h create mode 100644 test/Index/Inputs/preamble-with-implicit-import-C.h create mode 100644 test/Index/Inputs/preamble-with-implicit-import.h create mode 100644 test/Index/preamble-with-implicit-import.m diff --git a/lib/Basic/Module.cpp b/lib/Basic/Module.cpp index a8656749907..b4f8d252d21 100644 --- a/lib/Basic/Module.cpp +++ b/lib/Basic/Module.cpp @@ -497,6 +497,7 @@ void Module::dump() const { void VisibleModuleSet::setVisible(Module *M, SourceLocation Loc, VisibleCallback Vis, ConflictCallback Cb) { + assert(Loc.isValid() && "setVisible expects a valid import location"); if (isVisible(M)) return; diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index b61265bccb8..efb16748590 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -4052,7 +4052,9 @@ void ASTReader::InitializeContext() { if (Module *Imported = getSubmodule(Import.ID)) { makeModuleVisible(Imported, Module::AllVisible, /*ImportLoc=*/Import.ImportLoc); - PP.makeModuleVisible(Imported, Import.ImportLoc); + if (Import.ImportLoc.isValid()) + PP.makeModuleVisible(Imported, Import.ImportLoc); + // FIXME: should we tell Sema to make the module visible too? } } ImportedModules.clear(); diff --git a/test/Index/Inputs/module.map b/test/Index/Inputs/module.map index 4bfc109a8b1..10712accb1c 100644 --- a/test/Index/Inputs/module.map +++ b/test/Index/Inputs/module.map @@ -6,3 +6,17 @@ module ModuleNeedsVFS { framework module * { } module ModuleUndef { header "module-undef.h" } + +module PreambleWithImplicitImport { + module A { + header "preamble-with-implicit-import-A.h" + } + module B { + header "preamble-with-implicit-import-B.h" + export * + } + module C { + header "preamble-with-implicit-import-C.h" + export * + } +} diff --git a/test/Index/Inputs/preamble-with-implicit-import-A.h b/test/Index/Inputs/preamble-with-implicit-import-A.h new file mode 100644 index 00000000000..c6839015909 --- /dev/null +++ b/test/Index/Inputs/preamble-with-implicit-import-A.h @@ -0,0 +1 @@ +// preamble-with-implicit-import-A diff --git a/test/Index/Inputs/preamble-with-implicit-import-B.h b/test/Index/Inputs/preamble-with-implicit-import-B.h new file mode 100644 index 00000000000..17c138dfb5a --- /dev/null +++ b/test/Index/Inputs/preamble-with-implicit-import-B.h @@ -0,0 +1,3 @@ +#pragma once +#include "preamble-with-implicit-import-C.h" // Circular +typedef struct { char x; } Typo; diff --git a/test/Index/Inputs/preamble-with-implicit-import-C.h b/test/Index/Inputs/preamble-with-implicit-import-C.h new file mode 100644 index 00000000000..a3fc1d4fea0 --- /dev/null +++ b/test/Index/Inputs/preamble-with-implicit-import-C.h @@ -0,0 +1,2 @@ +#pragma once +#include "preamble-with-implicit-import-B.h" // Circular diff --git a/test/Index/Inputs/preamble-with-implicit-import.h b/test/Index/Inputs/preamble-with-implicit-import.h new file mode 100644 index 00000000000..1b429678f21 --- /dev/null +++ b/test/Index/Inputs/preamble-with-implicit-import.h @@ -0,0 +1,4 @@ +#include "preamble-with-implicit-import-A.h" + +// Typo is defined in B, which is not imported. +void useTypeFromB(Typo *); diff --git a/test/Index/preamble-with-implicit-import.m b/test/Index/preamble-with-implicit-import.m new file mode 100644 index 00000000000..e3d0e8b1a62 --- /dev/null +++ b/test/Index/preamble-with-implicit-import.m @@ -0,0 +1,6 @@ +// RUN: rm -rf %t +// RUN: env CINDEXTEST_EDITING=1 c-index-test -test-load-source-reparse 2 none %s -I %S/Inputs -fmodules -fmodules-cache-path=%t -fspell-checking 2>&1 | FileCheck %s +// CHECK: error: declaration of 'Typo' must be imported +// CHECK: error: declaration of 'Typo' must be imported + +#include "preamble-with-implicit-import.h" From dd28f1c2746959468d149595cbd0787f88aba8bf Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Thu, 11 Feb 2016 18:54:02 +0000 Subject: [PATCH 108/742] [Modules] Early-exit if ReadOptionsBlock fails to avoid crashing If we didn't tell ReadOptionsBlock to allow failures then we can't assume that the stream is not in the middle of a block if it returns out-of-date. This was causing a crash when we tried to continue reading. Also, it's just generally a good idea to early-exit if we're doing implicit module builds, since we will want to immediately rebuild this module anyway and there's no reason to waste time continuing after failure. rdar://problem/24114938 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260563 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Serialization/ASTReader.cpp | 7 ++++--- test/Modules/implicit-build-config-out-of-date.m | 6 ++++++ 2 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 test/Modules/implicit-build-config-out-of-date.m diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index efb16748590..b12c6b420be 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -2254,9 +2254,10 @@ ASTReader::ReadControlBlock(ModuleFile &F, (AllowConfigurationMismatch && Result == ConfigurationMismatch)) Result = Success; - // If we've diagnosed a problem, we're done. - if (Result != Success && - isDiagnosedResult(Result, ClientLoadCapabilities)) + // If we can't load the module, exit early since we likely + // will rebuild the module anyway. The stream may be in the + // middle of a block. + if (Result != Success) return Result; } else if (Stream.SkipBlock()) { Error("malformed block record in AST file"); diff --git a/test/Modules/implicit-build-config-out-of-date.m b/test/Modules/implicit-build-config-out-of-date.m new file mode 100644 index 00000000000..c8c02ff0a80 --- /dev/null +++ b/test/Modules/implicit-build-config-out-of-date.m @@ -0,0 +1,6 @@ +// RUN: rm -rf %t +// Use -DA=0 so that there is at least one preprocessor option serialized after the diagnostic options. +// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -fimplicit-module-maps -I %S/Inputs %s -DA=0 -Rmodule-build -verify +// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -fimplicit-module-maps -I %S/Inputs %s -DA=0 -Werror -Rmodule-build -verify + +@import category_top; // expected-remark {{building module}} expected-remark {{finished building}} From 26a9a1b2ff6a68d3de0954e9df28e7de5973c7a1 Mon Sep 17 00:00:00 2001 From: Manuel Klimek Date: Tue, 19 Jan 2016 14:05:32 +0000 Subject: [PATCH 109/742] Fix formatting of fully qualified names in array subscripts. Before: a[ ::b::c]; After: a[::b::c]; git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258123 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit fe46d7ff72602631f89c7c0e1ea183db06deffaa) --- lib/Format/TokenAnnotator.cpp | 3 ++- unittests/Format/FormatTest.cpp | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/Format/TokenAnnotator.cpp b/lib/Format/TokenAnnotator.cpp index 6d6df0cf401..9435ea9704a 100644 --- a/lib/Format/TokenAnnotator.cpp +++ b/lib/Format/TokenAnnotator.cpp @@ -2114,7 +2114,8 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, if (Right.is(tok::coloncolon) && Left.isNot(tok::l_brace)) return (Left.is(TT_TemplateOpener) && Style.Standard == FormatStyle::LS_Cpp03) || - !(Left.isOneOf(tok::identifier, tok::l_paren, tok::r_paren) || + !(Left.isOneOf(tok::identifier, tok::l_paren, tok::r_paren, + tok::l_square) || Left.isOneOf(TT_TemplateCloser, TT_TemplateOpener)); if ((Left.is(TT_TemplateOpener)) != (Right.is(TT_TemplateCloser))) return Style.SpacesInAngles; diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index d9a16db74c8..4e0d8d7acff 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -6133,6 +6133,7 @@ TEST_F(FormatTest, FormatsArrays) { "aaaaaaaaaaa aaaaaaaaaaaaaaa = aaaaaaaaaaaaaaaaaaaaaaaaaa->aaaaaaaaa[0]\n" " .aaaaaaa[0]\n" " .aaaaaaaaaaaaaaaaaaaaaa();"); + verifyFormat("a[::b::c];"); verifyNoCrash("a[,Y?)]", getLLVMStyleWithColumns(10)); From 104d5221b2f708632e93bc8d9ef669a150252910 Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Wed, 20 Jan 2016 15:25:30 +0000 Subject: [PATCH 110/742] Silencing several -Wcast-qual warnings; NFC. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258317 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 5944d95c2c79632face71a22e90d2aab053b4cb4) --- tools/libclang/CIndex.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index 79c7b628607..9963f328ff6 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -3583,7 +3583,8 @@ CXEvalResult clang_Cursor_Evaluate(CXCursor C) { expr = Field->getInClassInitializer(); } if (expr) - return (CXEvalResult)evaluateExpr((Expr *)expr, C); + return const_cast(reinterpret_cast( + evaluateExpr(const_cast(expr), C))); return nullptr; } @@ -3596,7 +3597,8 @@ CXEvalResult clang_Cursor_Evaluate(CXCursor C) { } } if (expr) - return (CXEvalResult)evaluateExpr(expr, C); + return const_cast( + reinterpret_cast(evaluateExpr(expr, C))); } return nullptr; } From cc784b60eef10ab9cebca2a597c37a33963ca311 Mon Sep 17 00:00:00 2001 From: Evgeniy Stepanov Date: Sat, 23 Jan 2016 01:20:18 +0000 Subject: [PATCH 111/742] [cfi] Do not emit bit set entry for available_externally vtables. In the Itanium ABI, vtable may be emitted speculatively as an available_externally global. Such vtable may not be present at the link time and should not have a corresponding CFI bit set entry. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258596 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 69bb2de9d80b5a3c577b46e6036449c4d7a05391) --- lib/CodeGen/ItaniumCXXABI.cpp | 3 ++- test/CodeGenCXX/cfi-speculative-vtable.cpp | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 test/CodeGenCXX/cfi-speculative-vtable.cpp diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp index 8b6322a4b75..e2106342922 100644 --- a/lib/CodeGen/ItaniumCXXABI.cpp +++ b/lib/CodeGen/ItaniumCXXABI.cpp @@ -1496,7 +1496,8 @@ void ItaniumCXXABI::emitVTableDefinitions(CodeGenVTables &CGVT, DC->getParent()->isTranslationUnit()) EmitFundamentalRTTIDescriptors(); - CGM.EmitVTableBitSetEntries(VTable, VTLayout); + if (!VTable->isDeclarationForLinker()) + CGM.EmitVTableBitSetEntries(VTable, VTLayout); } bool ItaniumCXXABI::isVirtualOffsetNeededForVTableField( diff --git a/test/CodeGenCXX/cfi-speculative-vtable.cpp b/test/CodeGenCXX/cfi-speculative-vtable.cpp new file mode 100644 index 00000000000..490190c4afd --- /dev/null +++ b/test/CodeGenCXX/cfi-speculative-vtable.cpp @@ -0,0 +1,14 @@ +// Test that we don't emit a bit set entry for a speculative (available_externally) vtable. +// This does not happen in the Microsoft ABI. +// RUN: %clang_cc1 -triple x86_64-unknown-linux -O1 -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux -O1 -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -fsanitize-cfi-cross-dso -emit-llvm -o - %s | FileCheck %s + +class A { + public: + virtual ~A(); +}; + +A a; + +// CHECK: @_ZTV1A ={{.*}} available_externally +// CHECK-NOT: !{{.*}} = !{!{{.*}}, [4 x i8*]* @_ZTV1A, i64 16} From 3a6f3632414e44af4523ca81b31e20d088abf28d Mon Sep 17 00:00:00 2001 From: David Majnemer Date: Tue, 26 Jan 2016 01:37:01 +0000 Subject: [PATCH 112/742] [Sema] Incomplete types are OK for covariant returns Per C++14 [class.virtual]p8, it is OK for the return type's class type to be incomplete so long as the return type is the same between the base and complete classes. This fixes PR26297. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258768 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 5ef36534d758f2a40270d355026a40635f438ab7) --- lib/Sema/SemaDeclCXX.cpp | 25 +++++++++++++------------ test/SemaCXX/virtual-override.cpp | 12 ++++++++++++ 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 10f6c1268a0..6b57c0a85ad 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -13023,19 +13023,20 @@ bool Sema::CheckOverridingFunctionReturnType(const CXXMethodDecl *New, return true; } - // C++ [class.virtual]p6: - // If the return type of D::f differs from the return type of B::f, the - // class type in the return type of D::f shall be complete at the point of - // declaration of D::f or shall be the class type D. - if (const RecordType *RT = NewClassTy->getAs()) { - if (!RT->isBeingDefined() && - RequireCompleteType(New->getLocation(), NewClassTy, - diag::err_covariant_return_incomplete, - New->getDeclName())) - return true; - } - if (!Context.hasSameUnqualifiedType(NewClassTy, OldClassTy)) { + // C++14 [class.virtual]p8: + // If the class type in the covariant return type of D::f differs from + // that of B::f, the class type in the return type of D::f shall be + // complete at the point of declaration of D::f or shall be the class + // type D. + if (const RecordType *RT = NewClassTy->getAs()) { + if (!RT->isBeingDefined() && + RequireCompleteType(New->getLocation(), NewClassTy, + diag::err_covariant_return_incomplete, + New->getDeclName())) + return true; + } + // Check if the new class derives from the old class. if (!IsDerivedFrom(New->getLocation(), NewClassTy, OldClassTy)) { Diag(New->getLocation(), diag::err_covariant_return_not_derived) diff --git a/test/SemaCXX/virtual-override.cpp b/test/SemaCXX/virtual-override.cpp index ec884f36323..42491178482 100644 --- a/test/SemaCXX/virtual-override.cpp +++ b/test/SemaCXX/virtual-override.cpp @@ -289,3 +289,15 @@ namespace PR8168 { static void foo() {} // expected-error{{'static' member function 'foo' overrides a virtual function}} }; } + +namespace PR26297 { +struct Incomplete; + +struct Base { + virtual const Incomplete *meow() = 0; +}; + +struct Derived : Base { + virtual Incomplete *meow() override { return nullptr; } +}; +} From d1adaf9392b80a7a489099d4382da6da6b458245 Mon Sep 17 00:00:00 2001 From: David Majnemer Date: Tue, 26 Jan 2016 01:39:17 +0000 Subject: [PATCH 113/742] [Sema] Remove stray semicolons. No functional change is intended. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258769 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit b59d4bdbdff38b8d8c772fc41c67c7855d1e4249) --- lib/Sema/SemaDeclCXX.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 6b57c0a85ad..48466af92ec 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -13073,7 +13073,7 @@ bool Sema::CheckOverridingFunctionReturnType(const CXXMethodDecl *New, Diag(Old->getLocation(), diag::note_overridden_virtual_function) << Old->getReturnTypeSourceRange(); return true; - }; + } // The new class type must have the same or less qualifiers as the old type. @@ -13085,7 +13085,7 @@ bool Sema::CheckOverridingFunctionReturnType(const CXXMethodDecl *New, Diag(Old->getLocation(), diag::note_overridden_virtual_function) << Old->getReturnTypeSourceRange(); return true; - }; + } return false; } From d597572a42efa9c4f226a3ef14068ee89230349d Mon Sep 17 00:00:00 2001 From: Eric Christopher Date: Fri, 29 Jan 2016 01:35:53 +0000 Subject: [PATCH 114/742] Use a consistent spelling for vtables. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259137 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit eae9579a0112068afbad767932c379ded643771b) --- include/clang/Basic/TargetCXXABI.h | 2 +- lib/CodeGen/CGClass.cpp | 2 +- lib/CodeGen/CGObjCMac.cpp | 2 +- lib/CodeGen/CGVTables.cpp | 24 ++++++++++----------- lib/CodeGen/CodeGenModule.cpp | 6 +++--- lib/CodeGen/ItaniumCXXABI.cpp | 2 +- lib/CodeGen/MicrosoftCXXABI.cpp | 2 +- test/CodeGenCXX/vtable-key-function-arm.cpp | 6 +++--- test/CodeGenCXX/vtable-key-function-ios.cpp | 6 +++--- 9 files changed, 26 insertions(+), 26 deletions(-) diff --git a/include/clang/Basic/TargetCXXABI.h b/include/clang/Basic/TargetCXXABI.h index 67247ead2eb..f7d4b920ca1 100644 --- a/include/clang/Basic/TargetCXXABI.h +++ b/include/clang/Basic/TargetCXXABI.h @@ -239,7 +239,7 @@ class TargetCXXABI { /// \brief Can an out-of-line inline function serve as a key function? /// /// This flag is only useful in ABIs where type data (for example, - /// v-tables and type_info objects) are emitted only after processing + /// vtables and type_info objects) are emitted only after processing /// the definition of a special "key" virtual function. (This is safe /// because the ODR requires that every virtual function be defined /// somewhere in a program.) This usually permits such data to be diff --git a/lib/CodeGen/CGClass.cpp b/lib/CodeGen/CGClass.cpp index d55b73a4136..faf570c5126 100644 --- a/lib/CodeGen/CGClass.cpp +++ b/lib/CodeGen/CGClass.cpp @@ -95,7 +95,7 @@ CodeGenModule::getDynamicOffsetAlignment(CharUnits actualBaseAlign, // unless we someday add some sort of attribute to change the // assumed alignment of 'this'. So our goal here is pretty much // just to allow the user to explicitly say that a pointer is - // under-aligned and then safely access its fields and v-tables. + // under-aligned and then safely access its fields and vtables. if (actualBaseAlign >= expectedBaseAlign) { return expectedTargetAlign; } diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index b61d057f350..bde9279a6d6 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/lib/CodeGen/CGObjCMac.cpp @@ -6708,7 +6708,7 @@ static void appendSelectorForMessageRefTable(std::string &buffer, } } -/// Emit a "v-table" message send. We emit a weak hidden-visibility +/// Emit a "vtable" message send. We emit a weak hidden-visibility /// struct, initially containing the selector pointer and a pointer to /// a "fixup" variant of the appropriate objc_msgSend. To call, we /// load and call the function pointer, passing the address of the diff --git a/lib/CodeGen/CGVTables.cpp b/lib/CodeGen/CGVTables.cpp index a40aab29be0..620e9223b2e 100644 --- a/lib/CodeGen/CGVTables.cpp +++ b/lib/CodeGen/CGVTables.cpp @@ -719,7 +719,7 @@ static bool shouldEmitAvailableExternallyVTable(const CodeGenModule &CGM, CGM.getCXXABI().canSpeculativelyEmitVTable(RD); } -/// Compute the required linkage of the v-table for the given class. +/// Compute the required linkage of the vtable for the given class. /// /// Note that we only call this at the end of the translation unit. llvm::GlobalVariable::LinkageTypes @@ -804,7 +804,7 @@ CodeGenModule::getVTableLinkage(const CXXRecordDecl *RD) { llvm_unreachable("Invalid TemplateSpecializationKind!"); } -/// This is a callback from Sema to tell us that that a particular v-table is +/// This is a callback from Sema to tell us that that a particular vtable is /// required to be emitted in this translation unit. /// /// This is only called for vtables that _must_ be emitted (mainly due to key @@ -832,38 +832,38 @@ CodeGenVTables::GenerateClassData(const CXXRecordDecl *RD) { /// the translation unit. /// /// The only semantic restriction here is that the object file should -/// not contain a v-table definition when that v-table is defined +/// not contain a vtable definition when that vtable is defined /// strongly elsewhere. Otherwise, we'd just like to avoid emitting -/// v-tables when unnecessary. +/// vtables when unnecessary. bool CodeGenVTables::isVTableExternal(const CXXRecordDecl *RD) { assert(RD->isDynamicClass() && "Non-dynamic classes have no VTable."); // If we have an explicit instantiation declaration (and not a - // definition), the v-table is defined elsewhere. + // definition), the vtable is defined elsewhere. TemplateSpecializationKind TSK = RD->getTemplateSpecializationKind(); if (TSK == TSK_ExplicitInstantiationDeclaration) return true; // Otherwise, if the class is an instantiated template, the - // v-table must be defined here. + // vtable must be defined here. if (TSK == TSK_ImplicitInstantiation || TSK == TSK_ExplicitInstantiationDefinition) return false; // Otherwise, if the class doesn't have a key function (possibly - // anymore), the v-table must be defined here. + // anymore), the vtable must be defined here. const CXXMethodDecl *keyFunction = CGM.getContext().getCurrentKeyFunction(RD); if (!keyFunction) return false; // Otherwise, if we don't have a definition of the key function, the - // v-table must be defined somewhere else. + // vtable must be defined somewhere else. return !keyFunction->hasBody(); } /// Given that we're currently at the end of the translation unit, and -/// we've emitted a reference to the v-table for this class, should -/// we define that v-table? +/// we've emitted a reference to the vtable for this class, should +/// we define that vtable? static bool shouldEmitVTableAtEndOfTranslationUnit(CodeGenModule &CGM, const CXXRecordDecl *RD) { // If vtable is internal then it has to be done. @@ -875,7 +875,7 @@ static bool shouldEmitVTableAtEndOfTranslationUnit(CodeGenModule &CGM, } /// Given that at some point we emitted a reference to one or more -/// v-tables, and that we are now at the end of the translation unit, +/// vtables, and that we are now at the end of the translation unit, /// decide whether we should emit them. void CodeGenModule::EmitDeferredVTables() { #ifndef NDEBUG @@ -889,7 +889,7 @@ void CodeGenModule::EmitDeferredVTables() { VTables.GenerateClassData(RD); assert(savedSize == DeferredVTables.size() && - "deferred extra v-tables during v-table emission?"); + "deferred extra vtables during vtable emission?"); DeferredVTables.clear(); } diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index a89eb7ddf39..5c33a454c73 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -1229,13 +1229,13 @@ void CodeGenModule::EmitDeferred() { if (!DeferredVTables.empty()) { EmitDeferredVTables(); - // Emitting a v-table doesn't directly cause more v-tables to + // Emitting a vtable doesn't directly cause more vtables to // become deferred, although it can cause functions to be - // emitted that then need those v-tables. + // emitted that then need those vtables. assert(DeferredVTables.empty()); } - // Stop if we're out of both deferred v-tables and deferred declarations. + // Stop if we're out of both deferred vtables and deferred declarations. if (DeferredDeclsToEmit.empty()) return; diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp index e2106342922..753bf561e9b 100644 --- a/lib/CodeGen/ItaniumCXXABI.cpp +++ b/lib/CodeGen/ItaniumCXXABI.cpp @@ -1569,7 +1569,7 @@ llvm::GlobalVariable *ItaniumCXXABI::getAddrOfVTable(const CXXRecordDecl *RD, if (VTable) return VTable; - // Queue up this v-table for possible deferred emission. + // Queue up this vtable for possible deferred emission. CGM.addDeferredVTable(RD); SmallString<256> Name; diff --git a/lib/CodeGen/MicrosoftCXXABI.cpp b/lib/CodeGen/MicrosoftCXXABI.cpp index 93210d54d4b..71fc207e595 100644 --- a/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/lib/CodeGen/MicrosoftCXXABI.cpp @@ -1642,7 +1642,7 @@ llvm::GlobalVariable *MicrosoftCXXABI::getAddrOfVTable(const CXXRecordDecl *RD, if (DeferredVFTables.insert(RD).second) { // We haven't processed this record type before. - // Queue up this v-table for possible deferred emission. + // Queue up this vtable for possible deferred emission. CGM.addDeferredVTable(RD); #ifndef NDEBUG diff --git a/test/CodeGenCXX/vtable-key-function-arm.cpp b/test/CodeGenCXX/vtable-key-function-arm.cpp index 6f1265b6277..3d5c3c3865e 100644 --- a/test/CodeGenCXX/vtable-key-function-arm.cpp +++ b/test/CodeGenCXX/vtable-key-function-arm.cpp @@ -1,9 +1,9 @@ // RUN: %clang_cc1 %s -triple=armv7-unknown-unknown -emit-llvm -o - | FileCheck %s // RUN: %clang_cc1 %s -triple=armv7-unknown-unknown -emit-llvm -o - | FileCheck -check-prefix=CHECK-LATE %s -// The 'a' variants ask for the v-table first. -// The 'b' variants ask for the v-table second. -// The 'c' variants ask for the v-table third. +// The 'a' variants ask for the vtable first. +// The 'b' variants ask for the vtable second. +// The 'c' variants ask for the vtable third. // We do a separate CHECK-LATE pass because the RTTI definition gets // changed after the fact, which causes reordering of the globals. diff --git a/test/CodeGenCXX/vtable-key-function-ios.cpp b/test/CodeGenCXX/vtable-key-function-ios.cpp index d17aa695d2b..8a3466beda4 100644 --- a/test/CodeGenCXX/vtable-key-function-ios.cpp +++ b/test/CodeGenCXX/vtable-key-function-ios.cpp @@ -4,9 +4,9 @@ // RUN: %clang_cc1 %s -triple=x86_64-pc-windows-gnu -emit-llvm -o - | FileCheck %s // RUN: %clang_cc1 %s -triple=x86_64-pc-windows-gnu -emit-llvm -o - | FileCheck -check-prefix=CHECK-LATE %s -// The 'a' variants ask for the v-table first. -// The 'b' variants ask for the v-table second. -// The 'c' variants ask for the v-table third. +// The 'a' variants ask for the vtable first. +// The 'b' variants ask for the vtable second. +// The 'c' variants ask for the vtable third. // We do a separate CHECK-LATE pass because the RTTI definition gets // changed after the fact, which causes reordering of the globals. From 42edc887f23c82b87ffc4a51a22e8b5bacfe462c Mon Sep 17 00:00:00 2001 From: Eric Christopher Date: Fri, 29 Jan 2016 01:35:55 +0000 Subject: [PATCH 115/742] Add the clang debug info test directory to .gitignore as it's managed separately. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259138 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit a6ed23a04708602d087bb542a5214803af628884) --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 3d07e81baf3..fc842489a03 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,5 @@ tools/extra # Sphinx build products docs/_build docs/analyzer/_build +# debug info testsuite +test/debuginfo-tests From 3003d538913cce7e1f8f1f588cf6bc0b3fb40438 Mon Sep 17 00:00:00 2001 From: Yaron Keren Date: Fri, 29 Jan 2016 13:46:15 +0000 Subject: [PATCH 116/742] Implement TemplateArgument::dump() method for debugging, patterned after TemplateName::dump(). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259192 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit d8a9f87f7cde461fd0c9cdf8e7d7863471185237) --- include/clang/AST/TemplateBase.h | 6 ++++++ lib/AST/TemplateBase.cpp | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/include/clang/AST/TemplateBase.h b/include/clang/AST/TemplateBase.h index f87171a81d1..b9c2c08943e 100644 --- a/include/clang/AST/TemplateBase.h +++ b/include/clang/AST/TemplateBase.h @@ -354,6 +354,12 @@ class TemplateArgument { /// \brief Print this template argument to the given output stream. void print(const PrintingPolicy &Policy, raw_ostream &Out) const; + /// \brief Debugging aid that dumps the template argument. + void dump(raw_ostream &Out) const; + + /// \brief Debugging aid that dumps the template argument to standard error. + void dump() const; + /// \brief Used to insert TemplateArguments into FoldingSets. void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context) const; }; diff --git a/lib/AST/TemplateBase.cpp b/lib/AST/TemplateBase.cpp index e9edb0df66d..f79ce9765a6 100644 --- a/lib/AST/TemplateBase.cpp +++ b/lib/AST/TemplateBase.cpp @@ -415,6 +415,15 @@ void TemplateArgument::print(const PrintingPolicy &Policy, } } +void TemplateArgument::dump(raw_ostream &Out) const { + LangOptions LO; // FIXME! see also TemplateName::dump(). + LO.CPlusPlus = true; + LO.Bool = true; + print(PrintingPolicy(LO), Out); +} + +void TemplateArgument::dump() const { dump(llvm::errs()); } + //===----------------------------------------------------------------------===// // TemplateArgumentLoc Implementation //===----------------------------------------------------------------------===// From 88bde7eecf98f9239c452a61b5f683504d4cb6fa Mon Sep 17 00:00:00 2001 From: Yaron Keren Date: Fri, 29 Jan 2016 19:38:18 +0000 Subject: [PATCH 117/742] Annotate dump() methods with LLVM_DUMP_METHOD, addressing Richard Smith r259192 post commit comment. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259232 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit dcbc03e44635014437d6745bc8bef7ae066c945b) --- lib/AST/APValue.cpp | 2 +- lib/AST/DeclarationName.cpp | 2 +- lib/AST/NestedNameSpecifier.cpp | 2 +- lib/AST/TemplateBase.cpp | 2 +- lib/AST/TemplateName.cpp | 2 +- lib/Analysis/CFG.cpp | 2 +- lib/Analysis/CallGraph.cpp | 4 ++-- lib/Basic/Module.cpp | 2 +- lib/CodeGen/CGRecordLayoutBuilder.cpp | 4 ++-- lib/CodeGen/TargetInfo.cpp | 2 +- lib/Frontend/LayoutOverrideSource.cpp | 2 +- lib/Lex/HeaderMap.cpp | 2 +- lib/Lex/MacroInfo.cpp | 4 ++-- lib/Lex/ModuleMap.cpp | 2 +- lib/Sema/Scope.cpp | 2 +- lib/Sema/SemaInit.cpp | 2 +- lib/Sema/SemaOverload.cpp | 2 +- lib/Serialization/ASTReader.cpp | 2 +- lib/Serialization/GlobalModuleIndex.cpp | 2 +- lib/Serialization/Module.cpp | 2 +- lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp | 2 +- lib/StaticAnalyzer/Core/BugReporter.cpp | 10 +++++----- lib/StaticAnalyzer/Core/MemRegion.cpp | 4 ++-- lib/StaticAnalyzer/Core/ProgramState.cpp | 2 +- lib/StaticAnalyzer/Core/SVals.cpp | 2 +- lib/StaticAnalyzer/Core/SymbolManager.cpp | 2 +- 26 files changed, 34 insertions(+), 34 deletions(-) diff --git a/lib/AST/APValue.cpp b/lib/AST/APValue.cpp index 91f1e20d73b..3c587331ed0 100644 --- a/lib/AST/APValue.cpp +++ b/lib/AST/APValue.cpp @@ -255,7 +255,7 @@ void APValue::swap(APValue &RHS) { memcpy(RHS.Data.buffer, TmpData, DataSize); } -void APValue::dump() const { +LLVM_DUMP_METHOD void APValue::dump() const { dump(llvm::errs()); llvm::errs() << '\n'; } diff --git a/lib/AST/DeclarationName.cpp b/lib/AST/DeclarationName.cpp index b2f27275f49..8322b607a6c 100644 --- a/lib/AST/DeclarationName.cpp +++ b/lib/AST/DeclarationName.cpp @@ -333,7 +333,7 @@ DeclarationName DeclarationName::getUsingDirectiveName() { return DeclarationName(Ptr); } -void DeclarationName::dump() const { +LLVM_DUMP_METHOD void DeclarationName::dump() const { llvm::errs() << *this << '\n'; } diff --git a/lib/AST/NestedNameSpecifier.cpp b/lib/AST/NestedNameSpecifier.cpp index d2370c88b9c..ede38626c8b 100644 --- a/lib/AST/NestedNameSpecifier.cpp +++ b/lib/AST/NestedNameSpecifier.cpp @@ -322,7 +322,7 @@ void NestedNameSpecifier::dump(const LangOptions &LO) const { print(llvm::errs(), PrintingPolicy(LO)); } -void NestedNameSpecifier::dump() const { +LLVM_DUMP_METHOD void NestedNameSpecifier::dump() const { LangOptions LO; print(llvm::errs(), PrintingPolicy(LO)); } diff --git a/lib/AST/TemplateBase.cpp b/lib/AST/TemplateBase.cpp index f79ce9765a6..b75ede862f7 100644 --- a/lib/AST/TemplateBase.cpp +++ b/lib/AST/TemplateBase.cpp @@ -422,7 +422,7 @@ void TemplateArgument::dump(raw_ostream &Out) const { print(PrintingPolicy(LO), Out); } -void TemplateArgument::dump() const { dump(llvm::errs()); } +LLVM_DUMP_METHOD void TemplateArgument::dump() const { dump(llvm::errs()); } //===----------------------------------------------------------------------===// // TemplateArgumentLoc Implementation diff --git a/lib/AST/TemplateName.cpp b/lib/AST/TemplateName.cpp index 47e0255d52e..47a7d47e7a4 100644 --- a/lib/AST/TemplateName.cpp +++ b/lib/AST/TemplateName.cpp @@ -227,6 +227,6 @@ void TemplateName::dump(raw_ostream &OS) const { print(OS, PrintingPolicy(LO)); } -void TemplateName::dump() const { +LLVM_DUMP_METHOD void TemplateName::dump() const { dump(llvm::errs()); } diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp index ed2239f88ae..dc58b92090b 100644 --- a/lib/Analysis/CFG.cpp +++ b/lib/Analysis/CFG.cpp @@ -4514,7 +4514,7 @@ void CFGBlock::dump(const CFG* cfg, const LangOptions &LO, print(llvm::errs(), cfg, LO, ShowColors); } -void CFGBlock::dump() const { +LLVM_DUMP_METHOD void CFGBlock::dump() const { dump(getParent(), LangOptions(), false); } diff --git a/lib/Analysis/CallGraph.cpp b/lib/Analysis/CallGraph.cpp index d06603469dd..9d522fe7c6c 100644 --- a/lib/Analysis/CallGraph.cpp +++ b/lib/Analysis/CallGraph.cpp @@ -188,7 +188,7 @@ void CallGraph::print(raw_ostream &OS) const { OS.flush(); } -void CallGraph::dump() const { +LLVM_DUMP_METHOD void CallGraph::dump() const { print(llvm::errs()); } @@ -202,7 +202,7 @@ void CallGraphNode::print(raw_ostream &os) const { os << "< >"; } -void CallGraphNode::dump() const { +LLVM_DUMP_METHOD void CallGraphNode::dump() const { print(llvm::errs()); } diff --git a/lib/Basic/Module.cpp b/lib/Basic/Module.cpp index b4f8d252d21..275143fad55 100644 --- a/lib/Basic/Module.cpp +++ b/lib/Basic/Module.cpp @@ -491,7 +491,7 @@ void Module::print(raw_ostream &OS, unsigned Indent) const { OS << "}\n"; } -void Module::dump() const { +LLVM_DUMP_METHOD void Module::dump() const { print(llvm::errs()); } diff --git a/lib/CodeGen/CGRecordLayoutBuilder.cpp b/lib/CodeGen/CGRecordLayoutBuilder.cpp index d0381ef82d9..7d530a278fb 100644 --- a/lib/CodeGen/CGRecordLayoutBuilder.cpp +++ b/lib/CodeGen/CGRecordLayoutBuilder.cpp @@ -842,7 +842,7 @@ void CGRecordLayout::print(raw_ostream &OS) const { OS << "]>\n"; } -void CGRecordLayout::dump() const { +LLVM_DUMP_METHOD void CGRecordLayout::dump() const { print(llvm::errs()); } @@ -855,6 +855,6 @@ void CGBitFieldInfo::print(raw_ostream &OS) const { << " StorageOffset:" << StorageOffset.getQuantity() << ">"; } -void CGBitFieldInfo::dump() const { +LLVM_DUMP_METHOD void CGBitFieldInfo::dump() const { print(llvm::errs()); } diff --git a/lib/CodeGen/TargetInfo.cpp b/lib/CodeGen/TargetInfo.cpp index cdb0a6a168b..e0ed00f753e 100644 --- a/lib/CodeGen/TargetInfo.cpp +++ b/lib/CodeGen/TargetInfo.cpp @@ -130,7 +130,7 @@ bool ABIInfo::shouldSignExtUnsignedType(QualType Ty) const { return false; } -void ABIArgInfo::dump() const { +LLVM_DUMP_METHOD void ABIArgInfo::dump() const { raw_ostream &OS = llvm::errs(); OS << "(ABIArgInfo Kind="; switch (TheKind) { diff --git a/lib/Frontend/LayoutOverrideSource.cpp b/lib/Frontend/LayoutOverrideSource.cpp index 924a64068fe..06e9a7dc50b 100644 --- a/lib/Frontend/LayoutOverrideSource.cpp +++ b/lib/Frontend/LayoutOverrideSource.cpp @@ -188,7 +188,7 @@ LayoutOverrideSource::layoutRecordType(const RecordDecl *Record, return true; } -void LayoutOverrideSource::dump() { +LLVM_DUMP_METHOD void LayoutOverrideSource::dump() { raw_ostream &OS = llvm::errs(); for (llvm::StringMap::iterator L = Layouts.begin(), LEnd = Layouts.end(); diff --git a/lib/Lex/HeaderMap.cpp b/lib/Lex/HeaderMap.cpp index 09d53846d4c..0735d386e07 100644 --- a/lib/Lex/HeaderMap.cpp +++ b/lib/Lex/HeaderMap.cpp @@ -174,7 +174,7 @@ const char *HeaderMap::getString(unsigned StrTabIdx) const { //===----------------------------------------------------------------------===// /// dump - Print the contents of this headermap to stderr. -void HeaderMap::dump() const { +LLVM_DUMP_METHOD void HeaderMap::dump() const { const HMapHeader &Hdr = getHeader(); unsigned NumBuckets = getEndianAdjustedWord(Hdr.NumBuckets); diff --git a/lib/Lex/MacroInfo.cpp b/lib/Lex/MacroInfo.cpp index 0b4292fbeae..2ef4387b99b 100644 --- a/lib/Lex/MacroInfo.cpp +++ b/lib/Lex/MacroInfo.cpp @@ -126,7 +126,7 @@ bool MacroInfo::isIdenticalTo(const MacroInfo &Other, Preprocessor &PP, return true; } -void MacroInfo::dump() const { +LLVM_DUMP_METHOD void MacroInfo::dump() const { llvm::raw_ostream &Out = llvm::errs(); // FIXME: Dump locations. @@ -209,7 +209,7 @@ MacroDirective::findDirectiveAtLoc(SourceLocation L, SourceManager &SM) const { return DefInfo(); } -void MacroDirective::dump() const { +LLVM_DUMP_METHOD void MacroDirective::dump() const { llvm::raw_ostream &Out = llvm::errs(); switch (getKind()) { diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp index e14f8766ab1..db8deab2fae 100644 --- a/lib/Lex/ModuleMap.cpp +++ b/lib/Lex/ModuleMap.cpp @@ -870,7 +870,7 @@ void ModuleMap::setInferredModuleAllowedBy(Module *M, const FileEntry *ModMap) { InferredModuleAllowedBy[M] = ModMap; } -void ModuleMap::dump() { +LLVM_DUMP_METHOD void ModuleMap::dump() { llvm::errs() << "Modules:"; for (llvm::StringMap::iterator M = Modules.begin(), MEnd = Modules.end(); diff --git a/lib/Sema/Scope.cpp b/lib/Sema/Scope.cpp index 7c70048acfb..c9c7dea5d86 100644 --- a/lib/Sema/Scope.cpp +++ b/lib/Sema/Scope.cpp @@ -130,7 +130,7 @@ void Scope::mergeNRVOIntoParent() { getParent()->addNRVOCandidate(NRVO.getPointer()); } -void Scope::dump() const { dumpImpl(llvm::errs()); } +LLVM_DUMP_METHOD void Scope::dump() const { dumpImpl(llvm::errs()); } void Scope::dumpImpl(raw_ostream &OS) const { unsigned Flags = getFlags(); diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index c60ea865aa9..df92e930682 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -2928,7 +2928,7 @@ unsigned InitializedEntity::dumpImpl(raw_ostream &OS) const { return Depth + 1; } -void InitializedEntity::dump() const { +LLVM_DUMP_METHOD void InitializedEntity::dump() const { dumpImpl(llvm::errs()); } diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index c9293fa4c82..eaac06162f7 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -433,7 +433,7 @@ StandardConversionSequence::getNarrowingKind(ASTContext &Ctx, /// dump - Print this standard conversion sequence to standard /// error. Useful for debugging overloading issues. -void StandardConversionSequence::dump() const { +LLVM_DUMP_METHOD void StandardConversionSequence::dump() const { raw_ostream &OS = llvm::errs(); bool PrintedSomething = false; if (First != ICK_Identity) { diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index b12c6b420be..962733f41d0 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -6892,7 +6892,7 @@ dumpModuleIDMap(StringRef Name, } } -void ASTReader::dump() { +LLVM_DUMP_METHOD void ASTReader::dump() { llvm::errs() << "*** PCH/ModuleFile Remappings:\n"; dumpModuleIDMap("Global bit offset map", GlobalBitOffsetsMap); dumpModuleIDMap("Global source location entry map", GlobalSLocEntryMap); diff --git a/lib/Serialization/GlobalModuleIndex.cpp b/lib/Serialization/GlobalModuleIndex.cpp index af5f94a5cdc..581e894d915 100644 --- a/lib/Serialization/GlobalModuleIndex.cpp +++ b/lib/Serialization/GlobalModuleIndex.cpp @@ -354,7 +354,7 @@ void GlobalModuleIndex::printStats() { std::fprintf(stderr, "\n"); } -void GlobalModuleIndex::dump() { +LLVM_DUMP_METHOD void GlobalModuleIndex::dump() { llvm::errs() << "*** Global Module Index Dump:\n"; llvm::errs() << "Module files:\n"; for (auto &MI : Modules) { diff --git a/lib/Serialization/Module.cpp b/lib/Serialization/Module.cpp index 4884f0b0948..ca033b469b5 100644 --- a/lib/Serialization/Module.cpp +++ b/lib/Serialization/Module.cpp @@ -65,7 +65,7 @@ dumpLocalRemap(StringRef Name, } } -void ModuleFile::dump() { +LLVM_DUMP_METHOD void ModuleFile::dump() { llvm::errs() << "\nModule: " << FileName << "\n"; if (!Imports.empty()) { llvm::errs() << " Imports: "; diff --git a/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp b/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp index f4de733bd79..13f0f655b89 100644 --- a/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp +++ b/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp @@ -211,7 +211,7 @@ void ArrayBoundCheckerV2::reportOOB(CheckerContext &checkerContext, llvm::make_unique(*BT, os.str(), errorNode)); } -void RegionRawOffsetV2::dump() const { +LLVM_DUMP_METHOD void RegionRawOffsetV2::dump() const { dumpToStream(llvm::errs()); } diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp index 11be764633c..f546a6636d3 100644 --- a/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -3487,7 +3487,7 @@ LLVM_DUMP_METHOD void PathPieces::dump() const { } } -void PathDiagnosticCallPiece::dump() const { +LLVM_DUMP_METHOD void PathDiagnosticCallPiece::dump() const { llvm::errs() << "CALL\n--------------\n"; if (const Stmt *SLoc = getLocStmt(getLocation())) @@ -3498,26 +3498,26 @@ void PathDiagnosticCallPiece::dump() const { getLocation().dump(); } -void PathDiagnosticEventPiece::dump() const { +LLVM_DUMP_METHOD void PathDiagnosticEventPiece::dump() const { llvm::errs() << "EVENT\n--------------\n"; llvm::errs() << getString() << "\n"; llvm::errs() << " ---- at ----\n"; getLocation().dump(); } -void PathDiagnosticControlFlowPiece::dump() const { +LLVM_DUMP_METHOD void PathDiagnosticControlFlowPiece::dump() const { llvm::errs() << "CONTROL\n--------------\n"; getStartLocation().dump(); llvm::errs() << " ---- to ----\n"; getEndLocation().dump(); } -void PathDiagnosticMacroPiece::dump() const { +LLVM_DUMP_METHOD void PathDiagnosticMacroPiece::dump() const { llvm::errs() << "MACRO\n--------------\n"; // FIXME: Print which macro is being invoked. } -void PathDiagnosticLocation::dump() const { +LLVM_DUMP_METHOD void PathDiagnosticLocation::dump() const { if (!isValid()) { llvm::errs() << "\n"; return; diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp index 30052ccacee..e1cb2ad5ff0 100644 --- a/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -438,7 +438,7 @@ void SubRegion::anchor() { } // Region pretty-printing. //===----------------------------------------------------------------------===// -void MemRegion::dump() const { +LLVM_DUMP_METHOD void MemRegion::dump() const { dumpToStream(llvm::errs()); } @@ -525,7 +525,7 @@ void VarRegion::dumpToStream(raw_ostream &os) const { os << *cast(D); } -void RegionRawOffset::dump() const { +LLVM_DUMP_METHOD void RegionRawOffset::dump() const { dumpToStream(llvm::errs()); } diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp index 100fa75c5f4..adda7af08db 100644 --- a/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -439,7 +439,7 @@ void ProgramState::printDOT(raw_ostream &Out) const { print(Out, "\\l", "\\|"); } -void ProgramState::dump() const { +LLVM_DUMP_METHOD void ProgramState::dump() const { print(llvm::errs()); } diff --git a/lib/StaticAnalyzer/Core/SVals.cpp b/lib/StaticAnalyzer/Core/SVals.cpp index dffee6c8c57..a30beed688b 100644 --- a/lib/StaticAnalyzer/Core/SVals.cpp +++ b/lib/StaticAnalyzer/Core/SVals.cpp @@ -236,7 +236,7 @@ SVal loc::ConcreteInt::evalBinOp(BasicValueFactory& BasicVals, // Pretty-Printing. //===----------------------------------------------------------------------===// -void SVal::dump() const { dumpToStream(llvm::errs()); } +LLVM_DUMP_METHOD void SVal::dump() const { dumpToStream(llvm::errs()); } void SVal::dumpToStream(raw_ostream &os) const { switch (getBaseKind()) { diff --git a/lib/StaticAnalyzer/Core/SymbolManager.cpp b/lib/StaticAnalyzer/Core/SymbolManager.cpp index 2dd252c223f..b8b4af1179e 100644 --- a/lib/StaticAnalyzer/Core/SymbolManager.cpp +++ b/lib/StaticAnalyzer/Core/SymbolManager.cpp @@ -23,7 +23,7 @@ using namespace ento; void SymExpr::anchor() { } -void SymExpr::dump() const { +LLVM_DUMP_METHOD void SymExpr::dump() const { dumpToStream(llvm::errs()); } From 03621d77ec2c5aee18d2da5dbf5f25705ce8d88d Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Mon, 1 Feb 2016 11:20:55 +0000 Subject: [PATCH 118/742] clang-format: Fix alignment of trailing multiline columns. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259351 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 3136e21b4acbedec48c3ae43fbbd352ddc9ea661) --- lib/Format/WhitespaceManager.cpp | 24 ++++++++++++++---------- unittests/Format/FormatTest.cpp | 8 ++++++++ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/lib/Format/WhitespaceManager.cpp b/lib/Format/WhitespaceManager.cpp index d6e6ed2c2ba..0673dfb3ace 100644 --- a/lib/Format/WhitespaceManager.cpp +++ b/lib/Format/WhitespaceManager.cpp @@ -372,16 +372,20 @@ void WhitespaceManager::alignTrailingComments() { unsigned CommentColumn = SourceMgr.getSpellingColumnNumber( Changes[i].OriginalWhitespaceRange.getEnd()); for (unsigned j = i + 1; j != e; ++j) { - if (Changes[j].Kind != tok::comment) { // Skip over comments. - unsigned NextColumn = SourceMgr.getSpellingColumnNumber( - Changes[j].OriginalWhitespaceRange.getEnd()); - // The start of the next token was previously aligned with the - // start of this comment. - WasAlignedWithStartOfNextLine = - CommentColumn == NextColumn || - CommentColumn == NextColumn + Style.IndentWidth; - break; - } + if (Changes[j].Kind == tok::comment || + Changes[j].Kind == tok::unknown) + // Skip over comments and unknown tokens. "unknown tokens are used for + // the continuation of multiline comments. + continue; + + unsigned NextColumn = SourceMgr.getSpellingColumnNumber( + Changes[j].OriginalWhitespaceRange.getEnd()); + // The start of the next token was previously aligned with the + // start of this comment. + WasAlignedWithStartOfNextLine = + CommentColumn == NextColumn || + CommentColumn == NextColumn + Style.IndentWidth; + break; } } if (!Style.AlignTrailingComments || FollowsRBraceInColumn0) { diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index 4e0d8d7acff..057871b7021 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -961,6 +961,14 @@ TEST_F(FormatTest, UnderstandsSingleLineComments) { format("lineWith(); // comment\n" "// at start\n" "otherLine();")); + EXPECT_EQ("lineWith(); // comment\n" + "/*\n" + " * at start */\n" + "otherLine();", + format("lineWith(); // comment\n" + "/*\n" + " * at start */\n" + "otherLine();")); EXPECT_EQ("lineWith(); // comment\n" " // at start\n" "otherLine();", From afa54f098de6a3adb1276d38bca105fa16cac1fe Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Mon, 1 Feb 2016 11:21:02 +0000 Subject: [PATCH 119/742] clang-format: Add option to disable string literal formatting. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259352 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 03bf8df72b0db5fee9c4f08805e84a538fdf2267) --- include/clang/Format/Format.h | 4 ++++ lib/Format/ContinuationIndenter.cpp | 3 ++- lib/Format/Format.cpp | 6 +++++- unittests/Format/FormatTest.cpp | 6 ++++++ 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/include/clang/Format/Format.h b/include/clang/Format/Format.h index 6d051e09cb6..9f3f0326d45 100644 --- a/include/clang/Format/Format.h +++ b/include/clang/Format/Format.h @@ -285,6 +285,9 @@ struct FormatStyle { /// \brief Break after each annotation on a field in Java files. bool BreakAfterJavaFieldAnnotations; + /// \brief Allow breaking string literals when formatting. + bool BreakStringLiterals; + /// \brief The column limit. /// /// A column limit of \c 0 means that there is no column limit. In this case, @@ -619,6 +622,7 @@ struct FormatStyle { BreakConstructorInitializersBeforeComma == R.BreakConstructorInitializersBeforeComma && BreakAfterJavaFieldAnnotations == R.BreakAfterJavaFieldAnnotations && + BreakStringLiterals == R.BreakStringLiterals && ColumnLimit == R.ColumnLimit && CommentPragmas == R.CommentPragmas && ConstructorInitializerAllOnOneLineOrOnePerLine == R.ConstructorInitializerAllOnOneLineOrOnePerLine && diff --git a/lib/Format/ContinuationIndenter.cpp b/lib/Format/ContinuationIndenter.cpp index b820f53db52..8881a889da4 100644 --- a/lib/Format/ContinuationIndenter.cpp +++ b/lib/Format/ContinuationIndenter.cpp @@ -1060,7 +1060,8 @@ unsigned ContinuationIndenter::breakProtrudingToken(const FormatToken &Current, // FIXME: String literal breaking is currently disabled for Java and JS, as // it requires strings to be merged using "+" which we don't support. if (Style.Language == FormatStyle::LK_Java || - Style.Language == FormatStyle::LK_JavaScript) + Style.Language == FormatStyle::LK_JavaScript || + !Style.BreakStringLiterals) return 0; // Don't break string literals inside preprocessor directives (except for diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp index 2689368da51..acd520ef546 100644 --- a/lib/Format/Format.cpp +++ b/lib/Format/Format.cpp @@ -275,6 +275,9 @@ template <> struct MappingTraits { Style.BreakBeforeTernaryOperators); IO.mapOptional("BreakConstructorInitializersBeforeComma", Style.BreakConstructorInitializersBeforeComma); + IO.mapOptional("BreakAfterJavaFieldAnnotations", + Style.BreakAfterJavaFieldAnnotations); + IO.mapOptional("BreakStringLiterals", Style.BreakStringLiterals); IO.mapOptional("ColumnLimit", Style.ColumnLimit); IO.mapOptional("CommentPragmas", Style.CommentPragmas); IO.mapOptional("ConstructorInitializerAllOnOneLineOrOnePerLine", @@ -488,8 +491,9 @@ FormatStyle getLLVMStyle() { LLVMStyle.BreakBeforeBraces = FormatStyle::BS_Attach; LLVMStyle.BraceWrapping = {false, false, false, false, false, false, false, false, false, false, false}; - LLVMStyle.BreakConstructorInitializersBeforeComma = false; LLVMStyle.BreakAfterJavaFieldAnnotations = false; + LLVMStyle.BreakConstructorInitializersBeforeComma = false; + LLVMStyle.BreakStringLiterals = true; LLVMStyle.ColumnLimit = 80; LLVMStyle.CommentPragmas = "^ IWYU pragma:"; LLVMStyle.ConstructorInitializerAllOnOneLineOrOnePerLine = false; diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index 057871b7021..17db98fe79f 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -7958,6 +7958,10 @@ TEST_F(FormatTest, BreaksStringLiterals) { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"});", getGoogleStyle())); + FormatStyle Style = getLLVMStyleWithColumns(12); + Style.BreakStringLiterals = false; + EXPECT_EQ("\"some text other\";", format("\"some text other\";", Style)); + FormatStyle AlignLeft = getLLVMStyleWithColumns(12); AlignLeft.AlignEscapedNewlinesLeft = true; EXPECT_EQ("#define A \\\n" @@ -9823,8 +9827,10 @@ TEST_F(FormatTest, ParsesConfigurationBools) { CHECK_PARSE_BOOL(AlwaysBreakTemplateDeclarations); CHECK_PARSE_BOOL(BinPackArguments); CHECK_PARSE_BOOL(BinPackParameters); + CHECK_PARSE_BOOL(BreakAfterJavaFieldAnnotations); CHECK_PARSE_BOOL(BreakBeforeTernaryOperators); CHECK_PARSE_BOOL(BreakConstructorInitializersBeforeComma); + CHECK_PARSE_BOOL(BreakStringLiterals); CHECK_PARSE_BOOL(ConstructorInitializerAllOnOneLineOrOnePerLine); CHECK_PARSE_BOOL(DerivePointerAlignment); CHECK_PARSE_BOOL_FIELD(DerivePointerAlignment, "DerivePointerBinding"); From 3ed6fcf0d16afc30556407ae82c237e4c9eef78e Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Mon, 1 Feb 2016 11:21:07 +0000 Subject: [PATCH 120/742] clang-format: Fix incorrect pointer detection in lambdas in constructor initializers. Before: Constructor() : member([](A *a, B * b) {}) {} After: Constructor() : member([](A *a, B *b) {}) {} git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259353 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 4e199700be2453f8d63826e005875843428c9839) --- lib/Format/TokenAnnotator.cpp | 4 ++-- unittests/Format/FormatTest.cpp | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/Format/TokenAnnotator.cpp b/lib/Format/TokenAnnotator.cpp index 9435ea9704a..e3bf20adf87 100644 --- a/lib/Format/TokenAnnotator.cpp +++ b/lib/Format/TokenAnnotator.cpp @@ -887,8 +887,8 @@ class AnnotatingParser { Previous && Previous->isOneOf(tok::star, tok::amp); Previous = Previous->Previous) Previous->Type = TT_PointerOrReference; - if (Line.MustBeDeclaration) - Contexts.back().IsExpression = Contexts.front().InCtorInitializer; + if (Line.MustBeDeclaration && !Contexts.front().InCtorInitializer) + Contexts.back().IsExpression = false; } else if (Current.Previous && Current.Previous->is(TT_CtorInitializerColon)) { Contexts.back().IsExpression = true; diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index 17db98fe79f..ad355398508 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -5634,6 +5634,7 @@ TEST_F(FormatTest, UnderstandsUsesOfStarAndAmp) { verifyFormat("[](const decltype(*a) &value) {}"); verifyFormat("decltype(a * b) F();"); verifyFormat("#define MACRO() [](A *a) { return 1; }"); + verifyFormat("Constructor() : member([](A *a, B *b) {}) {}"); verifyIndependentOfContext("typedef void (*f)(int *a);"); verifyIndependentOfContext("int i{a * b};"); verifyIndependentOfContext("aaa && aaa->f();"); From 2676550c59315f9bebd7452f1d079d9d03a46edb Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Tue, 2 Feb 2016 10:28:11 +0000 Subject: [PATCH 121/742] clang-format: Make AlignAfterOpenBracket also affect angle brackets. Patch by Matthew Whitehead, thank you. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259487 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 2b9f6f3342bd454ccf0918a2c8c11d28f27ca42e) --- lib/Format/ContinuationIndenter.cpp | 3 ++- unittests/Format/FormatTest.cpp | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/Format/ContinuationIndenter.cpp b/lib/Format/ContinuationIndenter.cpp index 8881a889da4..4f9064c871a 100644 --- a/lib/Format/ContinuationIndenter.cpp +++ b/lib/Format/ContinuationIndenter.cpp @@ -352,7 +352,8 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun, // disallowing any further line breaks if there is no line break after the // opening parenthesis. Don't break if it doesn't conserve columns. if (Style.AlignAfterOpenBracket == FormatStyle::BAS_AlwaysBreak && - Previous.is(tok::l_paren) && State.Column > getNewLineColumn(State) && + Previous.isOneOf(tok::l_paren, TT_TemplateOpener, tok::l_square) && + State.Column > getNewLineColumn(State) && (!Previous.Previous || !Previous.Previous->isOneOf(tok::kw_for, tok::kw_while, tok::kw_switch))) State.Stack.back().NoLineBreak = true; diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index ad355398508..6ffeb65280a 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -10325,6 +10325,15 @@ TEST_F(FormatTest, ConstructorInitializerIndentWidth) { ": aaaaaaaaaaaaa(aaaaaaaaaaaaaa), aaaaaaaaaaaaa(aaaaaaaaaaaaaa),\n" " aaaaaaaaaaaaa(aaaaaaaaaaaaaa) {}", Style); + Style.AlignAfterOpenBracket = FormatStyle::BAS_AlwaysBreak; + verifyFormat( + "SomeLongTemplateVariableName<\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa>", + Style); + verifyFormat( + "bool smaller = 1 < bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb(\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);", + Style); } TEST_F(FormatTest, BreakConstructorInitializersBeforeComma) { From cdae3cc1d1dcd247ab60245ed07f6eb349e47ba2 Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Wed, 3 Feb 2016 17:27:10 +0000 Subject: [PATCH 122/742] clang-format: Fix formatting of ternary expressions with comments. Before: int i = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ? /*bbbbbbbbbbbbbbbbbbbb=*/bbbbbbbbbbbbbbbbbbbbbbbbb : ccccccccccccccccccccccccccc; After: int i = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ? /*bbbbbbbbbbbbbbbbbbbb=*/bbbbbbbbbbbbbbbbbbbbbbbbb : ccccccccccccccccccccccccccc; git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259670 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 7d018c0578a34bf413106bf72c47152b2cb83937) --- lib/Format/ContinuationIndenter.cpp | 12 ++++++++---- unittests/Format/FormatTest.cpp | 4 ++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/Format/ContinuationIndenter.cpp b/lib/Format/ContinuationIndenter.cpp index 4f9064c871a..c712ed5bd07 100644 --- a/lib/Format/ContinuationIndenter.cpp +++ b/lib/Format/ContinuationIndenter.cpp @@ -704,11 +704,15 @@ unsigned ContinuationIndenter::moveStateToNextToken(LineState &State, if (Current.is(TT_ArraySubscriptLSquare) && State.Stack.back().StartOfArraySubscripts == 0) State.Stack.back().StartOfArraySubscripts = State.Column; - if ((Current.is(tok::question) && Style.BreakBeforeTernaryOperators) || - (Current.getPreviousNonComment() && Current.isNot(tok::colon) && - Current.getPreviousNonComment()->is(tok::question) && - !Style.BreakBeforeTernaryOperators)) + if (Style.BreakBeforeTernaryOperators && Current.is(tok::question)) State.Stack.back().QuestionColumn = State.Column; + if (!Style.BreakBeforeTernaryOperators && Current.isNot(tok::colon)) { + const FormatToken *Previous = Current.Previous; + while (Previous && Previous->isTrailingComment()) + Previous = Previous->Previous; + if (Previous && Previous->is(tok::question)) + State.Stack.back().QuestionColumn = State.Column; + } if (!Current.opensScope() && !Current.closesScope()) State.LowestLevelOnLine = std::min(State.LowestLevelOnLine, Current.NestingLevel); diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index 6ffeb65280a..e03a484f290 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -4694,6 +4694,10 @@ TEST_F(FormatTest, BreaksConditionalExpressionsAfterOperator) { " ccccccccccccccc :\n" " ddddddddddddddd);", Style); + verifyFormat("int i = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ?\n" + " /*bbbbbbbbbbbbbbb=*/bbbbbbbbbbbbbbbbbbbbbbbbb :\n" + " ccccccccccccccccccccccccccc;", + Style); } TEST_F(FormatTest, DeclarationsOfMultipleVariables) { From 8f76d9494da840f350960270d1705295ecc63c73 Mon Sep 17 00:00:00 2001 From: Alexey Bataev Date: Thu, 4 Feb 2016 04:22:09 +0000 Subject: [PATCH 123/742] PR23057: fix use-after-free due to local token buffer in ParseCXXAmbiguousParenExpression, by Dmitry Polukhin Differential Revision: http://reviews.llvm.org/D16572 A test/Parser/cxx-ambig-paren-expr-asan.cpp M lib/Parse/ParseExprCXX.cpp git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259750 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 2fa0d3e203ac0ac3db8422a3db3e6e91747e3033) --- lib/Parse/ParseExprCXX.cpp | 20 +++++++++++++++++++- test/Parser/cxx-ambig-paren-expr-asan.cpp | 9 +++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 test/Parser/cxx-ambig-paren-expr-asan.cpp diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index 53009caf8c7..760c30b8124 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -3081,6 +3081,14 @@ Parser::ParseCXXAmbiguousParenExpression(ParenParseOption &ExprType, ParseAs = NotCastExpr ? SimpleExpr : CastExpr; } + // Create a fake EOF to mark end of Toks buffer. + Token AttrEnd; + AttrEnd.startToken(); + AttrEnd.setKind(tok::eof); + AttrEnd.setLocation(Tok.getLocation()); + AttrEnd.setEofData(Toks.data()); + Toks.push_back(AttrEnd); + // The current token should go after the cached tokens. Toks.push_back(Tok); // Re-enter the stored parenthesized tokens into the token stream, so we may @@ -3105,6 +3113,10 @@ Parser::ParseCXXAmbiguousParenExpression(ParenParseOption &ExprType, Tracker.consumeClose(); ColonProt.restore(); + // Consume EOF marker for Toks buffer. + assert(Tok.is(tok::eof) && Tok.getEofData() == AttrEnd.getEofData()); + ConsumeAnyToken(); + if (ParseAs == CompoundLiteral) { ExprType = CompoundLiteral; if (DeclaratorInfo.isInvalidType()) @@ -3141,10 +3153,16 @@ Parser::ParseCXXAmbiguousParenExpression(ParenParseOption &ExprType, // Match the ')'. if (Result.isInvalid()) { - SkipUntil(tok::r_paren, StopAtSemi); + while (Tok.isNot(tok::eof)) + ConsumeAnyToken(); + assert(Tok.getEofData() == AttrEnd.getEofData()); + ConsumeAnyToken(); return ExprError(); } Tracker.consumeClose(); + // Consume EOF marker for Toks buffer. + assert(Tok.is(tok::eof) && Tok.getEofData() == AttrEnd.getEofData()); + ConsumeAnyToken(); return Result; } diff --git a/test/Parser/cxx-ambig-paren-expr-asan.cpp b/test/Parser/cxx-ambig-paren-expr-asan.cpp new file mode 100644 index 00000000000..ec9d6b9da39 --- /dev/null +++ b/test/Parser/cxx-ambig-paren-expr-asan.cpp @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -fsyntax-only -pedantic -verify %s + +// This syntax error used to cause use-after free due to token local buffer +// in ParseCXXAmbiguousParenExpression. +int H((int()[)]); +// expected-error@-1 {{expected expression}} +// expected-error@-2 {{expected ']'}} +// expected-note@-3 {{to match this '['}} +// expected-error@-4 {{expected ';' after top level declarator}} From f108ccc359b5ab06719a63d7860b3933499af4d2 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 4 Feb 2016 22:54:41 +0000 Subject: [PATCH 124/742] PR25271: When attaching default template arguments to redeclarations of a template, keep looking for default arguments if we see a template parameter pack. There may be default arguments preceding a pack with no default argument. Patch by Jannis Harder! git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259836 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit aaf9f44a0d006d13bcbc91dd9718d269cfa3682c) --- lib/Serialization/ASTReaderDecl.cpp | 2 ++ ...variadic-templates-with-default-params.cpp | 26 +++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 test/PCH/cxx-variadic-templates-with-default-params.cpp diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index 5bf95f878d4..82124e913e8 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -3000,6 +3000,8 @@ static void inheritDefaultTemplateArguments(ASTContext &Context, for (unsigned I = 0, N = FromTP->size(); I != N; ++I) { NamedDecl *FromParam = FromTP->getParam(N - I - 1); + if (FromParam->isParameterPack()) + continue; NamedDecl *ToParam = ToTP->getParam(N - I - 1); if (auto *FTTP = dyn_cast(FromParam)) { diff --git a/test/PCH/cxx-variadic-templates-with-default-params.cpp b/test/PCH/cxx-variadic-templates-with-default-params.cpp new file mode 100644 index 00000000000..2c1482091db --- /dev/null +++ b/test/PCH/cxx-variadic-templates-with-default-params.cpp @@ -0,0 +1,26 @@ +// Test this without pch. +// RUN: %clang_cc1 -std=c++11 -include %s -fsyntax-only -verify %s + +// Test with pch. +// RUN: %clang_cc1 -std=c++11 -x c++-header -emit-pch -o %t %s +// RUN: %clang_cc1 -std=c++11 -include-pch %t -fsyntax-only -verify %s + +// expected-no-diagnostics + +// PR25271: Ensure that default template arguments prior to a parameter pack +// successfully round-trip. +#ifndef HEADER +#define HEADER +template +class dummy; + +template +class dummy { + int field[T]; +}; +#else +void f() { + dummy<> x; + (void)x; +} +#endif From d099b60d02dff934bdb3860237072d265f65bc99 Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Fri, 5 Feb 2016 14:17:16 +0000 Subject: [PATCH 125/742] clang-format: Fix corner case in template detection. Before: f(a.operator() < A > ()); After: f(a.operator()()); git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259884 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 508fd615f6d875d09332a57a9b9f085f724c41b8) --- lib/Format/ContinuationIndenter.cpp | 2 +- lib/Format/TokenAnnotator.cpp | 21 +++++++++++++++------ unittests/Format/FormatTest.cpp | 4 ++++ 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/lib/Format/ContinuationIndenter.cpp b/lib/Format/ContinuationIndenter.cpp index c712ed5bd07..695fde01b91 100644 --- a/lib/Format/ContinuationIndenter.cpp +++ b/lib/Format/ContinuationIndenter.cpp @@ -249,7 +249,7 @@ bool ContinuationIndenter::mustBreak(const LineState &State) { // If the return type spans multiple lines, wrap before the function name. if ((Current.is(TT_FunctionDeclarationName) || (Current.is(tok::kw_operator) && !Previous.is(tok::coloncolon))) && - State.Stack.back().BreakBeforeParameter) + !Previous.is(tok::kw_template) && State.Stack.back().BreakBeforeParameter) return true; if (startsSegmentOfBuilderTypeCall(Current) && diff --git a/lib/Format/TokenAnnotator.cpp b/lib/Format/TokenAnnotator.cpp index e3bf20adf87..d10536c452c 100644 --- a/lib/Format/TokenAnnotator.cpp +++ b/lib/Format/TokenAnnotator.cpp @@ -42,8 +42,21 @@ class AnnotatingParser { private: bool parseAngle() { - if (!CurrentToken) + if (!CurrentToken || !CurrentToken->Previous) + return false; + if (NonTemplateLess.count(CurrentToken->Previous)) return false; + + const FormatToken& Previous = *CurrentToken->Previous; + if (Previous.Previous) { + if (Previous.Previous->Tok.isLiteral()) + return false; + if (Previous.Previous->is(tok::r_paren) && Contexts.size() > 1 && + (!Previous.Previous->MatchingParen || + !Previous.Previous->MatchingParen->is(TT_OverloadedOperatorLParen))) + return false; + } + FormatToken *Left = CurrentToken->Previous; Left->ParentBracket = Contexts.back().ContextKind; ScopedContextCreator ContextCreator(*this, tok::less, 10); @@ -550,11 +563,7 @@ class AnnotatingParser { return false; break; case tok::less: - if (!NonTemplateLess.count(Tok) && - (!Tok->Previous || - (!Tok->Previous->Tok.isLiteral() && - !(Tok->Previous->is(tok::r_paren) && Contexts.size() > 1))) && - parseAngle()) { + if (parseAngle()) { Tok->Type = TT_TemplateOpener; } else { Tok->Type = TT_BinaryOperator; diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index e03a484f290..0db5d9c89a1 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -5394,6 +5394,10 @@ TEST_F(FormatTest, UnderstandsTemplateParameters) { verifyFormat("struct A::type>;"); verifyFormat("template struct S{}> {};"); + verifyFormat("f(a.operator()());"); + verifyFormat("f(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + " .template operator()());", + getLLVMStyleWithColumns(35)); // Not template parameters. verifyFormat("return a < b && c > d;"); From 5e0fc4b7bd6ee900100decc85543d48f3fc6a12d Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Mon, 8 Feb 2016 09:52:54 +0000 Subject: [PATCH 126/742] clang-format: Fix weird alignment when not aligning after brackets. Before: bbbbbbbbbbbb(aaaaaaaaaaaaaaaaaaaaaaaa, // ccccccc(aaaaaaaaaaaaaaaaa, // b)); After: bbbbbbbbbbbb(aaaaaaaaaaaaaaaaaaaaaaaa, // ccccccc(aaaaaaaaaaaaaaaaa, // b)); This fixes llvm.org/PR24905. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260080 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 7bb97b213b5337c6c5cafefa2f452d0c23730e75) --- lib/Format/ContinuationIndenter.cpp | 3 ++- unittests/Format/FormatTest.cpp | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/Format/ContinuationIndenter.cpp b/lib/Format/ContinuationIndenter.cpp index 695fde01b91..12729245cf4 100644 --- a/lib/Format/ContinuationIndenter.cpp +++ b/lib/Format/ContinuationIndenter.cpp @@ -862,7 +862,8 @@ void ContinuationIndenter::moveStatePastFakeLParens(LineState &State, // ParameterToInnerFunction)); if (*I > prec::Unknown) NewParenState.LastSpace = std::max(NewParenState.LastSpace, State.Column); - if (*I != prec::Conditional && !Current.is(TT_UnaryOperator)) + if (*I != prec::Conditional && !Current.is(TT_UnaryOperator) && + Style.AlignAfterOpenBracket != FormatStyle::BAS_DontAlign) NewParenState.StartOfFunctionCall = State.Column; // Always indent conditional expressions. Never indent expression where diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index 0db5d9c89a1..8d541301a33 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -4434,6 +4434,11 @@ TEST_F(FormatTest, AlignsAfterOpenBracket) { " aaaaaaaaaaaaaaaaaaaaa, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa));", Style); + verifyFormat("bbbbbbbbbbbb(aaaaaaaaaaaaaaaaaaaaaaaa, //\n" + " ccccccc(aaaaaaaaaaaaaaaaa, //\n" + " b));", + Style); + Style.AlignAfterOpenBracket = FormatStyle::BAS_AlwaysBreak; Style.BinPackArguments = false; Style.BinPackParameters = false; From 4651c3549ee110856fce7af280ecf447e50c5de0 Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Thu, 11 Feb 2016 06:43:01 +0000 Subject: [PATCH 127/742] clang-format: Make it more expensive to break template parameters. In particular, make it more expensive than breaking after the return type of a function definition/declaration. Before: template aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaa< T>::aaaaaaaaaaaaa(aaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaa); After: template aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaa::aaaaaaaaaaaaa( aaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaa); git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260497 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit c43284c4913e075fc10ac6dea2961788a660f09d) --- lib/Format/TokenAnnotator.cpp | 2 +- unittests/Format/FormatTest.cpp | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/Format/TokenAnnotator.cpp b/lib/Format/TokenAnnotator.cpp index d10536c452c..e3528b8a5f9 100644 --- a/lib/Format/TokenAnnotator.cpp +++ b/lib/Format/TokenAnnotator.cpp @@ -59,7 +59,7 @@ class AnnotatingParser { FormatToken *Left = CurrentToken->Previous; Left->ParentBracket = Contexts.back().ContextKind; - ScopedContextCreator ContextCreator(*this, tok::less, 10); + ScopedContextCreator ContextCreator(*this, tok::less, 12); // If this angle is in the context of an expression, we need to be more // hesitant to detect it as opening template parameters. diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index 8d541301a33..da8167ebf4c 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -3850,6 +3850,11 @@ TEST_F(FormatTest, BreaksFunctionDeclarations) { "typename aaaaaaaaaa::aaaaaaaaaaa\n" "aaaaaaaaaa::aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(\n" " bool *aaaaaaaaaaaaaaaaaa, bool *aa) {}"); + verifyGoogleFormat( + "template \n" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" + "aaaaaaaaaaaaaaaaaaaaaaa::aaaaaaaaaaaaa(\n" + " aaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaa);"); FormatStyle Style = getLLVMStyle(); Style.PointerAlignment = FormatStyle::PAS_Left; From ad482ce247ff29593ff7270768c21e85166c997c Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Thu, 11 Feb 2016 13:15:14 +0000 Subject: [PATCH 128/742] clang-format: Make indentation after "<<" more consistent. Before: Diag(aaaaaaaaaaaaaaaaaaaa, aaaaaaaa) << aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa( aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa); Diag(aaaaaaaaaaaaaaaaaaaa, aaaaaaaa) << aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa( aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) << aaa; After: Diag(aaaaaaaaaaaaaaaaaaaa, aaaaaaaa) << aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa( aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa); Diag(aaaaaaaaaaaaaaaaaaaa, aaaaaaaa) << aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa( aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) << aaa; git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260517 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit fbdfbfbe685900566c725a91c358579024f7e659) --- lib/Format/ContinuationIndenter.cpp | 12 +++++++++--- unittests/Format/FormatTest.cpp | 7 +++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/lib/Format/ContinuationIndenter.cpp b/lib/Format/ContinuationIndenter.cpp index 12729245cf4..52c806271fe 100644 --- a/lib/Format/ContinuationIndenter.cpp +++ b/lib/Format/ContinuationIndenter.cpp @@ -401,9 +401,9 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun, (Previous.isNot(tok::lessless) || Previous.OperatorIndex != 0 || Previous.NextOperator)) || Current.StartsBinaryExpression)) { - // Always indent relative to the RHS of the expression unless this is a - // simple assignment without binary expression on the RHS. Also indent - // relative to unary operators and the colons of constructor initializers. + // Indent relative to the RHS of the expression unless this is a simple + // assignment without binary expression on the RHS. Also indent relative to + // unary operators and the colons of constructor initializers. State.Stack.back().LastSpace = State.Column; } else if (Previous.is(TT_InheritanceColon)) { State.Stack.back().Indent = State.Column; @@ -530,6 +530,12 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State, if (!Current.isTrailingComment()) State.Stack.back().LastSpace = State.Column; + if (Current.is(tok::lessless)) + // If we are breaking before a "<<", we always want to indent relative to + // RHS. This is necessary only for "<<", as we special-case it and don't + // always indent relative to the RHS. + State.Stack.back().LastSpace += 3; // 3 -> width of "<< ". + State.StartOfLineLevel = Current.NestingLevel; State.LowestLevelOnLine = Current.NestingLevel; diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index da8167ebf4c..48269cae66d 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -5105,6 +5105,13 @@ TEST_F(FormatTest, AlignsPipes) { " << aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;"); verifyFormat("SemaRef.Diag(Loc, diag::note_for_range_begin_end)\n" " << BEF << IsTemplate << Description << E->getType();"); + verifyFormat("Diag(aaaaaaaaaaaaaaaaaaaa, aaaaaaaa)\n" + " << aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa);"); + verifyFormat("Diag(aaaaaaaaaaaaaaaaaaaa, aaaaaaaa)\n" + " << aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(\n" + " aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa)\n" + " << aaa;"); verifyFormat( "llvm::errs() << aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n" From 823d49d298839efa4ae7d6762f49199ee186147b Mon Sep 17 00:00:00 2001 From: Benjamin Kramer Date: Mon, 1 Feb 2016 13:22:39 +0000 Subject: [PATCH 129/742] Remove the egregious PCHContainer layering hack that doesn't seem to be necessary anymore. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259355 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit e4ebe1b3dbc16a911d865ef012a7d8ca38fc80cd) --- lib/Basic/FileManager.cpp | 5 ----- lib/Frontend/PCHContainerOperations.cpp | 3 +++ 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/Basic/FileManager.cpp b/lib/Basic/FileManager.cpp index cb3f75c25a0..ba016db011e 100644 --- a/lib/Basic/FileManager.cpp +++ b/lib/Basic/FileManager.cpp @@ -19,7 +19,6 @@ #include "clang/Basic/FileManager.h" #include "clang/Basic/FileSystemStatCache.h" -#include "clang/Frontend/PCHContainerOperations.h" #include "llvm/ADT/SmallString.h" #include "llvm/Config/llvm-config.h" #include "llvm/ADT/STLExtras.h" @@ -564,7 +563,3 @@ void FileManager::PrintStats() const { //llvm::errs() << PagesMapped << BytesOfPagesMapped << FSLookups; } - -// Virtual destructors for abstract base classes that need live in Basic. -PCHContainerWriter::~PCHContainerWriter() {} -PCHContainerReader::~PCHContainerReader() {} diff --git a/lib/Frontend/PCHContainerOperations.cpp b/lib/Frontend/PCHContainerOperations.cpp index 5e1d7720509..fd84678b808 100644 --- a/lib/Frontend/PCHContainerOperations.cpp +++ b/lib/Frontend/PCHContainerOperations.cpp @@ -19,6 +19,9 @@ using namespace clang; +PCHContainerWriter::~PCHContainerWriter() {} +PCHContainerReader::~PCHContainerReader() {} + namespace { /// \brief A PCHContainerGenerator that writes out the PCH to a flat file. From e02ba8a2ccce4ac421053b406588fc5ade6a2c19 Mon Sep 17 00:00:00 2001 From: Benjamin Kramer Date: Tue, 2 Feb 2016 11:06:51 +0000 Subject: [PATCH 130/742] Move DebugInfoKind into its own header to cut the cyclic dependency edge from Driver to Frontend. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259489 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 1dde17c5ad5a2b5876e8d12aa8e8d6db17f12fb3) --- include/clang/Driver/DebugInfoKind.h | 39 +++++++++++ include/clang/Frontend/CodeGenOptions.def | 2 +- include/clang/Frontend/CodeGenOptions.h | 25 +------- lib/CodeGen/BackendUtil.cpp | 2 +- lib/CodeGen/CGBlocks.cpp | 8 +-- lib/CodeGen/CGDebugInfo.cpp | 64 +++++++++---------- lib/CodeGen/CGDebugInfo.h | 2 +- lib/CodeGen/CGDecl.cpp | 10 +-- lib/CodeGen/CGOpenMPRuntime.cpp | 2 +- lib/CodeGen/CodeGenFunction.cpp | 2 +- lib/CodeGen/CodeGenModule.cpp | 11 ++-- .../ObjectFilePCHContainerOperations.cpp | 2 +- lib/Driver/Tools.cpp | 47 +++++++------- lib/Driver/Tools.h | 4 +- lib/Frontend/CompilerInvocation.cpp | 12 ++-- lib/Frontend/Rewrite/FrontendActions.cpp | 9 ++- 16 files changed, 126 insertions(+), 115 deletions(-) create mode 100644 include/clang/Driver/DebugInfoKind.h diff --git a/include/clang/Driver/DebugInfoKind.h b/include/clang/Driver/DebugInfoKind.h new file mode 100644 index 00000000000..0f84f339281 --- /dev/null +++ b/include/clang/Driver/DebugInfoKind.h @@ -0,0 +1,39 @@ +//===--- DebugInfoKind.h - Debug Info Emission Types ------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_DRIVER_DEBUGINFOKIND_H +#define LLVM_CLANG_DRIVER_DEBUGINFOKIND_H + +namespace clang { +namespace codegenoptions { + +enum DebugInfoKind { + NoDebugInfo, /// Don't generate debug info. + LocTrackingOnly, /// Emit location information but do not generate + /// debug info in the output. This is useful in + /// cases where the backend wants to track source + /// locations for instructions without actually + /// emitting debug info for them (e.g., when -Rpass + /// is used). + DebugLineTablesOnly, /// Emit only debug info necessary for generating + /// line number tables (-gline-tables-only). + LimitedDebugInfo, /// Limit generated debug info to reduce size + /// (-fno-standalone-debug). This emits + /// forward decls for types that could be + /// replaced with forward decls in the source + /// code. For dynamic C++ classes type info + /// is only emitted int the module that + /// contains the classe's vtable. + FullDebugInfo /// Generate complete debug info. +}; + +} // end namespace codegenoptions +} // end namespace clang + +#endif diff --git a/include/clang/Frontend/CodeGenOptions.def b/include/clang/Frontend/CodeGenOptions.def index 7a6a7dcfdaa..ba4cb520a82 100644 --- a/include/clang/Frontend/CodeGenOptions.def +++ b/include/clang/Frontend/CodeGenOptions.def @@ -185,7 +185,7 @@ VALUE_CODEGENOPT(NumRegisterParameters, 32, 0) VALUE_CODEGENOPT(SSPBufferSize, 32, 0) /// The kind of generated debug info. -ENUM_CODEGENOPT(DebugInfo, DebugInfoKind, 3, NoDebugInfo) +ENUM_CODEGENOPT(DebugInfo, codegenoptions::DebugInfoKind, 3, codegenoptions::NoDebugInfo) /// Tune the debug info for this debugger. ENUM_CODEGENOPT(DebuggerTuning, DebuggerKind, 2, DebuggerKindDefault) diff --git a/include/clang/Frontend/CodeGenOptions.h b/include/clang/Frontend/CodeGenOptions.h index e18b55c1bdc..6d13dd951b8 100644 --- a/include/clang/Frontend/CodeGenOptions.h +++ b/include/clang/Frontend/CodeGenOptions.h @@ -15,6 +15,7 @@ #define LLVM_CLANG_FRONTEND_CODEGENOPTIONS_H #include "clang/Basic/Sanitizers.h" +#include "clang/Driver/DebugInfoKind.h" #include "llvm/Support/Regex.h" #include #include @@ -58,30 +59,6 @@ class CodeGenOptions : public CodeGenOptionsBase { Mixed = 2 }; - enum DebugInfoKind { - NoDebugInfo, /// Don't generate debug info. - - LocTrackingOnly, /// Emit location information but do not generate - /// debug info in the output. This is useful in - /// cases where the backend wants to track source - /// locations for instructions without actually - /// emitting debug info for them (e.g., when -Rpass - /// is used). - - DebugLineTablesOnly, /// Emit only debug info necessary for generating - /// line number tables (-gline-tables-only). - - LimitedDebugInfo, /// Limit generated debug info to reduce size - /// (-fno-standalone-debug). This emits - /// forward decls for types that could be - /// replaced with forward decls in the source - /// code. For dynamic C++ classes type info - /// is only emitted int the module that - /// contains the classe's vtable. - - FullDebugInfo /// Generate complete debug info. - }; - enum DebuggerKind { DebuggerKindDefault, DebuggerKindGDB, diff --git a/lib/CodeGen/BackendUtil.cpp b/lib/CodeGen/BackendUtil.cpp index 6d746c25eed..457af0611b2 100644 --- a/lib/CodeGen/BackendUtil.cpp +++ b/lib/CodeGen/BackendUtil.cpp @@ -425,7 +425,7 @@ void EmitAssemblyHelper::CreatePasses(FunctionInfoIndex *FunctionIndex) { !CodeGenOpts.CoverageNoFunctionNamesInData; Options.ExitBlockBeforeBody = CodeGenOpts.CoverageExitBlockBeforeBody; MPM->add(createGCOVProfilerPass(Options)); - if (CodeGenOpts.getDebugInfo() == CodeGenOptions::NoDebugInfo) + if (CodeGenOpts.getDebugInfo() == codegenoptions::NoDebugInfo) MPM->add(createStripSymbolsPass(true)); } diff --git a/lib/CodeGen/CGBlocks.cpp b/lib/CodeGen/CGBlocks.cpp index 742a5236e9f..a74294e7d62 100644 --- a/lib/CodeGen/CGBlocks.cpp +++ b/lib/CodeGen/CGBlocks.cpp @@ -1109,8 +1109,8 @@ void CodeGenFunction::setBlockContextParameter(const ImplicitParamDecl *D, } if (CGDebugInfo *DI = getDebugInfo()) { - if (CGM.getCodeGenOpts().getDebugInfo() - >= CodeGenOptions::LimitedDebugInfo) { + if (CGM.getCodeGenOpts().getDebugInfo() >= + codegenoptions::LimitedDebugInfo) { DI->setLocation(D->getLocation()); DI->EmitDeclareOfBlockLiteralArgVariable(*BlockInfo, arg, argNum, localAddr, Builder); @@ -1260,8 +1260,8 @@ CodeGenFunction::GenerateBlockFunction(GlobalDecl GD, const VarDecl *variable = CI.getVariable(); DI->EmitLocation(Builder, variable->getLocation()); - if (CGM.getCodeGenOpts().getDebugInfo() - >= CodeGenOptions::LimitedDebugInfo) { + if (CGM.getCodeGenOpts().getDebugInfo() >= + codegenoptions::LimitedDebugInfo) { const CGBlockInfo::Capture &capture = blockInfo.getCapture(variable); if (capture.isConstant()) { auto addr = LocalDeclMap.find(variable)->second; diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index 675b5eb07c5..7bd0e11439f 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -402,10 +402,10 @@ void CGDebugInfo::CreateCompileUnit() { LangTag, remapDIPath(MainFileName), remapDIPath(getCurrentDirname()), Producer, LO.Optimize, CGM.getCodeGenOpts().DwarfDebugFlags, RuntimeVers, CGM.getCodeGenOpts().SplitDwarfFile, - DebugKind <= CodeGenOptions::DebugLineTablesOnly + DebugKind <= codegenoptions::DebugLineTablesOnly ? llvm::DIBuilder::LineTablesOnly : llvm::DIBuilder::FullDebug, - 0 /* DWOid */, DebugKind != CodeGenOptions::LocTrackingOnly); + 0 /* DWOid */, DebugKind != codegenoptions::LocTrackingOnly); } llvm::DIType *CGDebugInfo::CreateType(const BuiltinType *BT) { @@ -1446,7 +1446,7 @@ void CGDebugInfo::CollectVTableInfo(const CXXRecordDecl *RD, llvm::DIFile *Unit, llvm::DIType *CGDebugInfo::getOrCreateRecordType(QualType RTy, SourceLocation Loc) { - assert(DebugKind >= CodeGenOptions::LimitedDebugInfo); + assert(DebugKind >= codegenoptions::LimitedDebugInfo); llvm::DIType *T = getOrCreateType(RTy, getOrCreateFile(Loc)); return T; } @@ -1458,7 +1458,7 @@ llvm::DIType *CGDebugInfo::getOrCreateInterfaceType(QualType D, llvm::DIType *CGDebugInfo::getOrCreateStandaloneType(QualType D, SourceLocation Loc) { - assert(DebugKind >= CodeGenOptions::LimitedDebugInfo); + assert(DebugKind >= codegenoptions::LimitedDebugInfo); assert(!D.isNull() && "null type"); llvm::DIType *T = getOrCreateType(D, getOrCreateFile(Loc)); assert(T && "could not create debug info for type"); @@ -1473,7 +1473,7 @@ llvm::DIType *CGDebugInfo::getOrCreateStandaloneType(QualType D, } void CGDebugInfo::completeType(const EnumDecl *ED) { - if (DebugKind <= CodeGenOptions::DebugLineTablesOnly) + if (DebugKind <= codegenoptions::DebugLineTablesOnly) return; QualType Ty = CGM.getContext().getEnumType(ED); void *TyPtr = Ty.getAsOpaquePtr(); @@ -1486,13 +1486,13 @@ void CGDebugInfo::completeType(const EnumDecl *ED) { } void CGDebugInfo::completeType(const RecordDecl *RD) { - if (DebugKind > CodeGenOptions::LimitedDebugInfo || + if (DebugKind > codegenoptions::LimitedDebugInfo || !CGM.getLangOpts().CPlusPlus) completeRequiredType(RD); } void CGDebugInfo::completeRequiredType(const RecordDecl *RD) { - if (DebugKind <= CodeGenOptions::DebugLineTablesOnly) + if (DebugKind <= codegenoptions::DebugLineTablesOnly) return; if (const CXXRecordDecl *CXXDecl = dyn_cast(RD)) @@ -1509,7 +1509,7 @@ void CGDebugInfo::completeRequiredType(const RecordDecl *RD) { } void CGDebugInfo::completeClassData(const RecordDecl *RD) { - if (DebugKind <= CodeGenOptions::DebugLineTablesOnly) + if (DebugKind <= codegenoptions::DebugLineTablesOnly) return; QualType Ty = CGM.getContext().getRecordType(RD); void *TyPtr = Ty.getAsOpaquePtr(); @@ -1531,16 +1531,15 @@ static bool hasExplicitMemberDefinition(CXXRecordDecl::method_iterator I, return false; } -static bool shouldOmitDefinition(CodeGenOptions::DebugInfoKind DebugKind, - bool DebugTypeExtRefs, - const RecordDecl *RD, +static bool shouldOmitDefinition(codegenoptions::DebugInfoKind DebugKind, + bool DebugTypeExtRefs, const RecordDecl *RD, const LangOptions &LangOpts) { // Does the type exist in an imported clang module? if (DebugTypeExtRefs && RD->isFromASTFile() && RD->getDefinition() && (RD->isExternallyVisible() || !RD->getName().empty())) return true; - if (DebugKind > CodeGenOptions::LimitedDebugInfo) + if (DebugKind > codegenoptions::LimitedDebugInfo) return false; if (!LangOpts.CPlusPlus) @@ -2172,7 +2171,7 @@ llvm::DIType *CGDebugInfo::getTypeOrNull(QualType Ty) { void CGDebugInfo::completeTemplateDefinition( const ClassTemplateSpecializationDecl &SD) { - if (DebugKind <= CodeGenOptions::DebugLineTablesOnly) + if (DebugKind <= codegenoptions::DebugLineTablesOnly) return; completeClassData(&SD); @@ -2438,13 +2437,12 @@ void CGDebugInfo::collectFunctionDeclProps(GlobalDecl GD, llvm::DIFile *Unit, // No need to replicate the linkage name if it isn't different from the // subprogram name, no need to have it at all unless coverage is enabled or // debug is set to more than just line tables. - if (LinkageName == Name || - (!CGM.getCodeGenOpts().EmitGcovArcs && - !CGM.getCodeGenOpts().EmitGcovNotes && - DebugKind <= CodeGenOptions::DebugLineTablesOnly)) + if (LinkageName == Name || (!CGM.getCodeGenOpts().EmitGcovArcs && + !CGM.getCodeGenOpts().EmitGcovNotes && + DebugKind <= codegenoptions::DebugLineTablesOnly)) LinkageName = StringRef(); - if (DebugKind >= CodeGenOptions::LimitedDebugInfo) { + if (DebugKind >= codegenoptions::LimitedDebugInfo) { if (const NamespaceDecl *NSDecl = dyn_cast_or_null(FD->getDeclContext())) FDContext = getOrCreateNameSpace(NSDecl); @@ -2580,7 +2578,7 @@ llvm::DINode *CGDebugInfo::getDeclarationOrDefinition(const Decl *D) { } llvm::DISubprogram *CGDebugInfo::getFunctionDeclaration(const Decl *D) { - if (!D || DebugKind <= CodeGenOptions::DebugLineTablesOnly) + if (!D || DebugKind <= codegenoptions::DebugLineTablesOnly) return nullptr; const FunctionDecl *FD = dyn_cast(D); @@ -2620,7 +2618,7 @@ llvm::DISubprogram *CGDebugInfo::getFunctionDeclaration(const Decl *D) { llvm::DISubroutineType *CGDebugInfo::getOrCreateFunctionType(const Decl *D, QualType FnType, llvm::DIFile *F) { - if (!D || DebugKind <= CodeGenOptions::DebugLineTablesOnly) + if (!D || DebugKind <= codegenoptions::DebugLineTablesOnly) // Create fake but valid subroutine type. Otherwise -verify would fail, and // subprogram DIE will miss DW_AT_decl_file and DW_AT_decl_line fields. return DBuilder.createSubroutineType(DBuilder.getOrCreateTypeArray(None)); @@ -2828,7 +2826,7 @@ void CGDebugInfo::EmitLexicalBlockStart(CGBuilderTy &Builder, Builder.SetCurrentDebugLocation(llvm::DebugLoc::get( getLineNumber(Loc), getColumnNumber(Loc), LexicalBlockStack.back())); - if (DebugKind <= CodeGenOptions::DebugLineTablesOnly) + if (DebugKind <= codegenoptions::DebugLineTablesOnly) return; // Create a new lexical block and push it on the stack. @@ -2842,7 +2840,7 @@ void CGDebugInfo::EmitLexicalBlockEnd(CGBuilderTy &Builder, // Provide an entry in the line table for the end of the block. EmitLocation(Builder, Loc); - if (DebugKind <= CodeGenOptions::DebugLineTablesOnly) + if (DebugKind <= codegenoptions::DebugLineTablesOnly) return; LexicalBlockStack.pop_back(); @@ -2937,7 +2935,7 @@ llvm::DIType *CGDebugInfo::EmitTypeForVarWithBlocksAttr(const VarDecl *VD, void CGDebugInfo::EmitDeclare(const VarDecl *VD, llvm::Value *Storage, llvm::Optional ArgNo, CGBuilderTy &Builder) { - assert(DebugKind >= CodeGenOptions::LimitedDebugInfo); + assert(DebugKind >= codegenoptions::LimitedDebugInfo); assert(!LexicalBlockStack.empty() && "Region stack mismatch, stack empty!"); bool Unwritten = @@ -3063,7 +3061,7 @@ void CGDebugInfo::EmitDeclare(const VarDecl *VD, llvm::Value *Storage, void CGDebugInfo::EmitDeclareOfAutoVariable(const VarDecl *VD, llvm::Value *Storage, CGBuilderTy &Builder) { - assert(DebugKind >= CodeGenOptions::LimitedDebugInfo); + assert(DebugKind >= codegenoptions::LimitedDebugInfo); EmitDeclare(VD, Storage, llvm::None, Builder); } @@ -3078,7 +3076,7 @@ llvm::DIType *CGDebugInfo::CreateSelfType(const QualType &QualTy, void CGDebugInfo::EmitDeclareOfBlockDeclRefVariable( const VarDecl *VD, llvm::Value *Storage, CGBuilderTy &Builder, const CGBlockInfo &blockInfo, llvm::Instruction *InsertPoint) { - assert(DebugKind >= CodeGenOptions::LimitedDebugInfo); + assert(DebugKind >= codegenoptions::LimitedDebugInfo); assert(!LexicalBlockStack.empty() && "Region stack mismatch, stack empty!"); if (Builder.GetInsertBlock() == nullptr) @@ -3146,7 +3144,7 @@ void CGDebugInfo::EmitDeclareOfBlockDeclRefVariable( void CGDebugInfo::EmitDeclareOfArgVariable(const VarDecl *VD, llvm::Value *AI, unsigned ArgNo, CGBuilderTy &Builder) { - assert(DebugKind >= CodeGenOptions::LimitedDebugInfo); + assert(DebugKind >= codegenoptions::LimitedDebugInfo); EmitDeclare(VD, AI, ArgNo, Builder); } @@ -3165,7 +3163,7 @@ void CGDebugInfo::EmitDeclareOfBlockLiteralArgVariable(const CGBlockInfo &block, unsigned ArgNo, llvm::Value *LocalAddr, CGBuilderTy &Builder) { - assert(DebugKind >= CodeGenOptions::LimitedDebugInfo); + assert(DebugKind >= codegenoptions::LimitedDebugInfo); ASTContext &C = CGM.getContext(); const BlockDecl *blockDecl = block.getBlockDecl(); @@ -3351,7 +3349,7 @@ llvm::DIGlobalVariable *CGDebugInfo::CollectAnonRecordDecls( void CGDebugInfo::EmitGlobalVariable(llvm::GlobalVariable *Var, const VarDecl *D) { - assert(DebugKind >= CodeGenOptions::LimitedDebugInfo); + assert(DebugKind >= codegenoptions::LimitedDebugInfo); // Create global variable debug descriptor. llvm::DIFile *Unit = nullptr; llvm::DIScope *DContext = nullptr; @@ -3383,7 +3381,7 @@ void CGDebugInfo::EmitGlobalVariable(llvm::GlobalVariable *Var, void CGDebugInfo::EmitGlobalVariable(const ValueDecl *VD, llvm::Constant *Init) { - assert(DebugKind >= CodeGenOptions::LimitedDebugInfo); + assert(DebugKind >= codegenoptions::LimitedDebugInfo); // Create the descriptor for the variable. llvm::DIFile *Unit = getOrCreateFile(VD->getLocation()); StringRef Name = VD->getName(); @@ -3430,7 +3428,7 @@ llvm::DIScope *CGDebugInfo::getCurrentContextDescriptor(const Decl *D) { } void CGDebugInfo::EmitUsingDirective(const UsingDirectiveDecl &UD) { - if (CGM.getCodeGenOpts().getDebugInfo() < CodeGenOptions::LimitedDebugInfo) + if (CGM.getCodeGenOpts().getDebugInfo() < codegenoptions::LimitedDebugInfo) return; const NamespaceDecl *NSDecl = UD.getNominatedNamespace(); if (!NSDecl->isAnonymousNamespace() || @@ -3443,7 +3441,7 @@ void CGDebugInfo::EmitUsingDirective(const UsingDirectiveDecl &UD) { } void CGDebugInfo::EmitUsingDecl(const UsingDecl &UD) { - if (CGM.getCodeGenOpts().getDebugInfo() < CodeGenOptions::LimitedDebugInfo) + if (CGM.getCodeGenOpts().getDebugInfo() < codegenoptions::LimitedDebugInfo) return; assert(UD.shadow_size() && "We shouldn't be codegening an invalid UsingDecl containing no decls"); @@ -3469,7 +3467,7 @@ void CGDebugInfo::EmitImportDecl(const ImportDecl &ID) { llvm::DIImportedEntity * CGDebugInfo::EmitNamespaceAlias(const NamespaceAliasDecl &NA) { - if (CGM.getCodeGenOpts().getDebugInfo() < CodeGenOptions::LimitedDebugInfo) + if (CGM.getCodeGenOpts().getDebugInfo() < codegenoptions::LimitedDebugInfo) return nullptr; auto &VH = NamespaceAliasCache[&NA]; if (VH) @@ -3564,7 +3562,7 @@ void CGDebugInfo::finalize() { } void CGDebugInfo::EmitExplicitCastType(QualType Ty) { - if (CGM.getCodeGenOpts().getDebugInfo() < CodeGenOptions::LimitedDebugInfo) + if (CGM.getCodeGenOpts().getDebugInfo() < codegenoptions::LimitedDebugInfo) return; if (auto *DieTy = getOrCreateType(Ty, getOrCreateMainFile())) diff --git a/lib/CodeGen/CGDebugInfo.h b/lib/CodeGen/CGDebugInfo.h index 55d813834e8..9e054618661 100644 --- a/lib/CodeGen/CGDebugInfo.h +++ b/lib/CodeGen/CGDebugInfo.h @@ -53,7 +53,7 @@ class CGDebugInfo { friend class ApplyDebugLocation; friend class SaveAndRestoreLocation; CodeGenModule &CGM; - const CodeGenOptions::DebugInfoKind DebugKind; + const codegenoptions::DebugInfoKind DebugKind; bool DebugTypeExtRefs; llvm::DIBuilder DBuilder; llvm::DICompileUnit *TheCU = nullptr; diff --git a/lib/CodeGen/CGDecl.cpp b/lib/CodeGen/CGDecl.cpp index cdf8f615cb1..e8a4d552923 100644 --- a/lib/CodeGen/CGDecl.cpp +++ b/lib/CodeGen/CGDecl.cpp @@ -394,7 +394,7 @@ void CodeGenFunction::EmitStaticVarDecl(const VarDecl &D, // Emit global variable debug descriptor for static vars. CGDebugInfo *DI = getDebugInfo(); if (DI && - CGM.getCodeGenOpts().getDebugInfo() >= CodeGenOptions::LimitedDebugInfo) { + CGM.getCodeGenOpts().getDebugInfo() >= codegenoptions::LimitedDebugInfo) { DI->setLocation(D.getLocation()); DI->EmitGlobalVariable(var, &D); } @@ -1085,8 +1085,8 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) { // Emit debug info for local var declaration. if (HaveInsertPoint()) if (CGDebugInfo *DI = getDebugInfo()) { - if (CGM.getCodeGenOpts().getDebugInfo() - >= CodeGenOptions::LimitedDebugInfo) { + if (CGM.getCodeGenOpts().getDebugInfo() >= + codegenoptions::LimitedDebugInfo) { DI->setLocation(D.getLocation()); DI->EmitDeclareOfAutoVariable(&D, address.getPointer(), Builder); } @@ -1851,8 +1851,8 @@ void CodeGenFunction::EmitParmDecl(const VarDecl &D, ParamValue Arg, // Emit debug info for param declaration. if (CGDebugInfo *DI = getDebugInfo()) { - if (CGM.getCodeGenOpts().getDebugInfo() - >= CodeGenOptions::LimitedDebugInfo) { + if (CGM.getCodeGenOpts().getDebugInfo() >= + codegenoptions::LimitedDebugInfo) { DI->EmitDeclareOfArgVariable(&D, DeclPtr.getPointer(), ArgNo, Builder); } } diff --git a/lib/CodeGen/CGOpenMPRuntime.cpp b/lib/CodeGen/CGOpenMPRuntime.cpp index 3b97ba2469a..246c553bdad 100644 --- a/lib/CodeGen/CGOpenMPRuntime.cpp +++ b/lib/CodeGen/CGOpenMPRuntime.cpp @@ -417,7 +417,7 @@ llvm::Value *CGOpenMPRuntime::emitUpdateLocation(CodeGenFunction &CGF, SourceLocation Loc, OpenMPLocationFlags Flags) { // If no debug info is generated - return global default location. - if (CGM.getCodeGenOpts().getDebugInfo() == CodeGenOptions::NoDebugInfo || + if (CGM.getCodeGenOpts().getDebugInfo() == codegenoptions::NoDebugInfo || Loc.isInvalid()) return getOrCreateDefaultLocation(Flags).getPointer(); diff --git a/lib/CodeGen/CodeGenFunction.cpp b/lib/CodeGen/CodeGenFunction.cpp index e41aa81bdef..e079b5869d9 100644 --- a/lib/CodeGen/CodeGenFunction.cpp +++ b/lib/CodeGen/CodeGenFunction.cpp @@ -1764,7 +1764,7 @@ void CodeGenFunction::EmitDeclRefExprDbgValue(const DeclRefExpr *E, llvm::Constant *Init) { assert (Init && "Invalid DeclRefExpr initializer!"); if (CGDebugInfo *Dbg = getDebugInfo()) - if (CGM.getCodeGenOpts().getDebugInfo() >= CodeGenOptions::LimitedDebugInfo) + if (CGM.getCodeGenOpts().getDebugInfo() >= codegenoptions::LimitedDebugInfo) Dbg->EmitGlobalVariable(E->getDecl(), Init); } diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index 5c33a454c73..32aa19418e3 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -137,9 +137,8 @@ CodeGenModule::CodeGenModule(ASTContext &C, const HeaderSearchOptions &HSO, // If debug info or coverage generation is enabled, create the CGDebugInfo // object. - if (CodeGenOpts.getDebugInfo() != CodeGenOptions::NoDebugInfo || - CodeGenOpts.EmitGcovArcs || - CodeGenOpts.EmitGcovNotes) + if (CodeGenOpts.getDebugInfo() != codegenoptions::NoDebugInfo || + CodeGenOpts.EmitGcovArcs || CodeGenOpts.EmitGcovNotes) DebugInfo = new CGDebugInfo(*this); Block.GlobalUniqueCount = 0; @@ -1696,7 +1695,7 @@ void CodeGenModule::CompleteDIClassType(const CXXMethodDecl* D) { return; if (CGDebugInfo *DI = getModuleDebugInfo()) - if (getCodeGenOpts().getDebugInfo() >= CodeGenOptions::LimitedDebugInfo) { + if (getCodeGenOpts().getDebugInfo() >= codegenoptions::LimitedDebugInfo) { const auto *ThisPtr = cast(D->getThisType(getContext())); DI->getOrCreateRecordType(ThisPtr->getPointeeType(), D->getLocation()); } @@ -2500,7 +2499,7 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D, // Emit global variable debug information. if (CGDebugInfo *DI = getModuleDebugInfo()) - if (getCodeGenOpts().getDebugInfo() >= CodeGenOptions::LimitedDebugInfo) + if (getCodeGenOpts().getDebugInfo() >= codegenoptions::LimitedDebugInfo) DI->EmitGlobalVariable(GV, D); } @@ -3658,7 +3657,7 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { ObjCRuntime->GenerateClass(OMD); // Emit global variable debug information. if (CGDebugInfo *DI = getModuleDebugInfo()) - if (getCodeGenOpts().getDebugInfo() >= CodeGenOptions::LimitedDebugInfo) + if (getCodeGenOpts().getDebugInfo() >= codegenoptions::LimitedDebugInfo) DI->getOrCreateInterfaceType(getContext().getObjCInterfaceType( OMD->getClassInterface()), OMD->getLocation()); break; diff --git a/lib/CodeGen/ObjectFilePCHContainerOperations.cpp b/lib/CodeGen/ObjectFilePCHContainerOperations.cpp index e3030a8e812..14c30dec755 100644 --- a/lib/CodeGen/ObjectFilePCHContainerOperations.cpp +++ b/lib/CodeGen/ObjectFilePCHContainerOperations.cpp @@ -151,7 +151,7 @@ class PCHContainerGenerator : public ASTConsumer { CodeGenOpts.CodeModel = "default"; CodeGenOpts.ThreadModel = "single"; CodeGenOpts.DebugTypeExtRefs = true; - CodeGenOpts.setDebugInfo(CodeGenOptions::FullDebugInfo); + CodeGenOpts.setDebugInfo(codegenoptions::FullDebugInfo); } ~PCHContainerGenerator() override = default; diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index 1d07fa0c045..0d62db737c6 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -2496,16 +2496,16 @@ static bool UseRelaxAll(Compilation &C, const ArgList &Args) { // Convert an arg of the form "-gN" or "-ggdbN" or one of their aliases // to the corresponding DebugInfoKind. -static CodeGenOptions::DebugInfoKind DebugLevelToInfoKind(const Arg &A) { +static codegenoptions::DebugInfoKind DebugLevelToInfoKind(const Arg &A) { assert(A.getOption().matches(options::OPT_gN_Group) && "Not a -g option that specifies a debug-info level"); if (A.getOption().matches(options::OPT_g0) || A.getOption().matches(options::OPT_ggdb0)) - return CodeGenOptions::NoDebugInfo; + return codegenoptions::NoDebugInfo; if (A.getOption().matches(options::OPT_gline_tables_only) || A.getOption().matches(options::OPT_ggdb1)) - return CodeGenOptions::DebugLineTablesOnly; - return CodeGenOptions::LimitedDebugInfo; + return codegenoptions::DebugLineTablesOnly; + return codegenoptions::LimitedDebugInfo; } // Extract the integer N from a string spelled "-dwarf-N", returning 0 @@ -2521,17 +2521,17 @@ static unsigned DwarfVersionNum(StringRef ArgValue) { } static void RenderDebugEnablingArgs(const ArgList &Args, ArgStringList &CmdArgs, - CodeGenOptions::DebugInfoKind DebugInfoKind, + codegenoptions::DebugInfoKind DebugInfoKind, unsigned DwarfVersion, llvm::DebuggerKind DebuggerTuning) { switch (DebugInfoKind) { - case CodeGenOptions::DebugLineTablesOnly: + case codegenoptions::DebugLineTablesOnly: CmdArgs.push_back("-debug-info-kind=line-tables-only"); break; - case CodeGenOptions::LimitedDebugInfo: + case codegenoptions::LimitedDebugInfo: CmdArgs.push_back("-debug-info-kind=limited"); break; - case CodeGenOptions::FullDebugInfo: + case codegenoptions::FullDebugInfo: CmdArgs.push_back("-debug-info-kind=standalone"); break; default: @@ -2668,9 +2668,9 @@ static void CollectArgsForIntegratedAssembler(Compilation &C, if (DwarfVersion == 0) { // Send it onward, and let cc1as complain. CmdArgs.push_back(Value.data()); } else { - RenderDebugEnablingArgs( - Args, CmdArgs, CodeGenOptions::LimitedDebugInfo, DwarfVersion, - llvm::DebuggerKind::Default); + RenderDebugEnablingArgs(Args, CmdArgs, + codegenoptions::LimitedDebugInfo, + DwarfVersion, llvm::DebuggerKind::Default); } } else if (Value.startswith("-mcpu") || Value.startswith("-mfpu") || Value.startswith("-mhwdiv") || Value.startswith("-march")) { @@ -4097,8 +4097,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, unsigned DwarfVersion = 0; llvm::DebuggerKind DebuggerTuning = getToolChain().getDefaultDebuggerTuning(); // These two are potentially updated by AddClangCLArgs. - enum CodeGenOptions::DebugInfoKind DebugInfoKind = - CodeGenOptions::NoDebugInfo; + codegenoptions::DebugInfoKind DebugInfoKind = codegenoptions::NoDebugInfo; bool EmitCodeView = false; // Add clang-cl arguments. @@ -4153,12 +4152,12 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, // If you say "-gsplit-dwarf -gline-tables-only", -gsplit-dwarf loses. // But -gsplit-dwarf is not a g_group option, hence we have to check the // order explicitly. (If -gsplit-dwarf wins, we fix DebugInfoKind later.) - if (SplitDwarfArg && DebugInfoKind < CodeGenOptions::LimitedDebugInfo && + if (SplitDwarfArg && DebugInfoKind < codegenoptions::LimitedDebugInfo && A->getIndex() > SplitDwarfArg->getIndex()) SplitDwarfArg = nullptr; } else // For any other 'g' option, use Limited. - DebugInfoKind = CodeGenOptions::LimitedDebugInfo; + DebugInfoKind = codegenoptions::LimitedDebugInfo; } // If a debugger tuning argument appeared, remember it. @@ -4183,7 +4182,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, // DwarfVersion remains at 0 if no explicit choice was made. CmdArgs.push_back("-gcodeview"); } else if (DwarfVersion == 0 && - DebugInfoKind != CodeGenOptions::NoDebugInfo) { + DebugInfoKind != codegenoptions::NoDebugInfo) { DwarfVersion = getToolChain().GetDefaultDwarfVersion(); } @@ -4197,7 +4196,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, // FIXME: Move backend command line options to the module. if (Args.hasArg(options::OPT_gmodules)) { - DebugInfoKind = CodeGenOptions::LimitedDebugInfo; + DebugInfoKind = codegenoptions::LimitedDebugInfo; CmdArgs.push_back("-dwarf-ext-refs"); CmdArgs.push_back("-fmodule-format=obj"); } @@ -4206,7 +4205,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, // splitting and extraction. // FIXME: Currently only works on Linux. if (getToolChain().getTriple().isOSLinux() && SplitDwarfArg) { - DebugInfoKind = CodeGenOptions::LimitedDebugInfo; + DebugInfoKind = codegenoptions::LimitedDebugInfo; CmdArgs.push_back("-backend-option"); CmdArgs.push_back("-split-dwarf=Enable"); } @@ -4219,8 +4218,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, bool NeedFullDebug = Args.hasFlag(options::OPT_fstandalone_debug, options::OPT_fno_standalone_debug, getToolChain().GetDefaultStandaloneDebug()); - if (DebugInfoKind == CodeGenOptions::LimitedDebugInfo && NeedFullDebug) - DebugInfoKind = CodeGenOptions::FullDebugInfo; + if (DebugInfoKind == codegenoptions::LimitedDebugInfo && NeedFullDebug) + DebugInfoKind = codegenoptions::FullDebugInfo; RenderDebugEnablingArgs(Args, CmdArgs, DebugInfoKind, DwarfVersion, DebuggerTuning); @@ -5834,7 +5833,7 @@ static EHFlags parseClangCLEHFlags(const Driver &D, const ArgList &Args) { } void Clang::AddClangCLArgs(const ArgList &Args, ArgStringList &CmdArgs, - enum CodeGenOptions::DebugInfoKind *DebugInfoKind, + codegenoptions::DebugInfoKind *DebugInfoKind, bool *EmitCodeView) const { unsigned RTOptionID = options::OPT__SLASH_MT; @@ -5904,7 +5903,7 @@ void Clang::AddClangCLArgs(const ArgList &Args, ArgStringList &CmdArgs, // If we are emitting CV but not DWARF, don't build information that LLVM // can't yet process. if (*EmitCodeView && !EmitDwarf) - *DebugInfoKind = CodeGenOptions::DebugLineTablesOnly; + *DebugInfoKind = codegenoptions::DebugLineTablesOnly; if (*EmitCodeView) CmdArgs.push_back("-gcodeview"); @@ -6068,8 +6067,8 @@ void ClangAs::ConstructJob(Compilation &C, const JobAction &JA, if (DwarfVersion == 0) DwarfVersion = getToolChain().GetDefaultDwarfVersion(); RenderDebugEnablingArgs(Args, CmdArgs, - (WantDebug ? CodeGenOptions::LimitedDebugInfo - : CodeGenOptions::NoDebugInfo), + (WantDebug ? codegenoptions::LimitedDebugInfo + : codegenoptions::NoDebugInfo), DwarfVersion, llvm::DebuggerKind::Default); // Add the -fdebug-compilation-dir flag if needed. diff --git a/lib/Driver/Tools.h b/lib/Driver/Tools.h index 69c046587f4..1b8176f8839 100644 --- a/lib/Driver/Tools.h +++ b/lib/Driver/Tools.h @@ -11,10 +11,10 @@ #define LLVM_CLANG_LIB_DRIVER_TOOLS_H #include "clang/Basic/VersionTuple.h" +#include "clang/Driver/DebugInfoKind.h" #include "clang/Driver/Tool.h" #include "clang/Driver/Types.h" #include "clang/Driver/Util.h" -#include "clang/Frontend/CodeGenOptions.h" #include "llvm/ADT/Triple.h" #include "llvm/Option/Option.h" #include "llvm/Support/Compiler.h" @@ -93,7 +93,7 @@ class LLVM_LIBRARY_VISIBILITY Clang : public Tool { void AddClangCLArgs(const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs, - enum CodeGenOptions::DebugInfoKind *DebugInfoKind, + codegenoptions::DebugInfoKind *DebugInfoKind, bool *EmitCodeView) const; visualstudio::Compiler *getCLFallback() const; diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index d6d1c792a8f..b27fd09b7da 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -416,15 +416,15 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, if (Arg *A = Args.getLastArg(OPT_debug_info_kind_EQ)) { unsigned Val = llvm::StringSwitch(A->getValue()) - .Case("line-tables-only", CodeGenOptions::DebugLineTablesOnly) - .Case("limited", CodeGenOptions::LimitedDebugInfo) - .Case("standalone", CodeGenOptions::FullDebugInfo) + .Case("line-tables-only", codegenoptions::DebugLineTablesOnly) + .Case("limited", codegenoptions::LimitedDebugInfo) + .Case("standalone", codegenoptions::FullDebugInfo) .Default(~0U); if (Val == ~0U) Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << A->getValue(); else - Opts.setDebugInfo(static_cast(Val)); + Opts.setDebugInfo(static_cast(Val)); } if (Arg *A = Args.getLastArg(OPT_debugger_tuning_EQ)) { unsigned Val = llvm::StringSwitch(A->getValue()) @@ -727,8 +727,8 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, // If the user requested a flag that requires source locations available in // the backend, make sure that the backend tracks source location information. - if (NeedLocTracking && Opts.getDebugInfo() == CodeGenOptions::NoDebugInfo) - Opts.setDebugInfo(CodeGenOptions::LocTrackingOnly); + if (NeedLocTracking && Opts.getDebugInfo() == codegenoptions::NoDebugInfo) + Opts.setDebugInfo(codegenoptions::LocTrackingOnly); Opts.RewriteMapFiles = Args.getAllArgValues(OPT_frewrite_map_file); diff --git a/lib/Frontend/Rewrite/FrontendActions.cpp b/lib/Frontend/Rewrite/FrontendActions.cpp index 8cf8adf37ed..d6e15689232 100644 --- a/lib/Frontend/Rewrite/FrontendActions.cpp +++ b/lib/Frontend/Rewrite/FrontendActions.cpp @@ -153,11 +153,10 @@ std::unique_ptr RewriteObjCAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { if (raw_ostream *OS = CI.createDefaultOutputFile(false, InFile, "cpp")) { if (CI.getLangOpts().ObjCRuntime.isNonFragile()) - return CreateModernObjCRewriter(InFile, OS, - CI.getDiagnostics(), CI.getLangOpts(), - CI.getDiagnosticOpts().NoRewriteMacros, - (CI.getCodeGenOpts().getDebugInfo() != - CodeGenOptions::NoDebugInfo)); + return CreateModernObjCRewriter( + InFile, OS, CI.getDiagnostics(), CI.getLangOpts(), + CI.getDiagnosticOpts().NoRewriteMacros, + (CI.getCodeGenOpts().getDebugInfo() != codegenoptions::NoDebugInfo)); return CreateObjCRewriter(InFile, OS, CI.getDiagnostics(), CI.getLangOpts(), CI.getDiagnosticOpts().NoRewriteMacros); From 8a3abd1e1913de6f30263dc203a5b913eead379b Mon Sep 17 00:00:00 2001 From: Paul Robinson Date: Fri, 5 Feb 2016 21:54:42 +0000 Subject: [PATCH 131/742] Move DebugInfoKind enum from Driver to Basic. NFC git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259935 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 8a9865e6ffaade033a3cd579491c70fcce2dff7e) --- .../{Driver/DebugInfoKind.h => Basic/DebugInfoOptions.h} | 6 +++--- include/clang/Frontend/CodeGenOptions.h | 2 +- lib/Driver/Tools.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename include/clang/{Driver/DebugInfoKind.h => Basic/DebugInfoOptions.h} (90%) diff --git a/include/clang/Driver/DebugInfoKind.h b/include/clang/Basic/DebugInfoOptions.h similarity index 90% rename from include/clang/Driver/DebugInfoKind.h rename to include/clang/Basic/DebugInfoOptions.h index 0f84f339281..e7ff4a662b6 100644 --- a/include/clang/Driver/DebugInfoKind.h +++ b/include/clang/Basic/DebugInfoOptions.h @@ -1,4 +1,4 @@ -//===--- DebugInfoKind.h - Debug Info Emission Types ------------*- C++ -*-===// +//===--- DebugInfoOptions.h - Debug Info Emission Types ---------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -7,8 +7,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_DRIVER_DEBUGINFOKIND_H -#define LLVM_CLANG_DRIVER_DEBUGINFOKIND_H +#ifndef LLVM_CLANG_BASIC_DEBUGINFOOPTIONS_H +#define LLVM_CLANG_BASIC_DEBUGINFOOPTIONS_H namespace clang { namespace codegenoptions { diff --git a/include/clang/Frontend/CodeGenOptions.h b/include/clang/Frontend/CodeGenOptions.h index 6d13dd951b8..ee6c51ba793 100644 --- a/include/clang/Frontend/CodeGenOptions.h +++ b/include/clang/Frontend/CodeGenOptions.h @@ -14,8 +14,8 @@ #ifndef LLVM_CLANG_FRONTEND_CODEGENOPTIONS_H #define LLVM_CLANG_FRONTEND_CODEGENOPTIONS_H +#include "clang/Basic/DebugInfoOptions.h" #include "clang/Basic/Sanitizers.h" -#include "clang/Driver/DebugInfoKind.h" #include "llvm/Support/Regex.h" #include #include diff --git a/lib/Driver/Tools.h b/lib/Driver/Tools.h index 1b8176f8839..1d348bbbd68 100644 --- a/lib/Driver/Tools.h +++ b/lib/Driver/Tools.h @@ -10,8 +10,8 @@ #ifndef LLVM_CLANG_LIB_DRIVER_TOOLS_H #define LLVM_CLANG_LIB_DRIVER_TOOLS_H +#include "clang/Basic/DebugInfoOptions.h" #include "clang/Basic/VersionTuple.h" -#include "clang/Driver/DebugInfoKind.h" #include "clang/Driver/Tool.h" #include "clang/Driver/Types.h" #include "clang/Driver/Util.h" From a949eab1847f59348cc10aafc556bdbee8e21943 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Sat, 6 Feb 2016 01:59:09 +0000 Subject: [PATCH 132/742] Fix a crash when emitting dbeug info for forward-declared scoped enums. It is possible for enums to be created as part of their own declcontext. We need to cache a placeholder to avoid the type being created twice before hitting the cache. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259975 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 6fbed68720badec2bcafeb98ecb554c681ae75ad) --- lib/CodeGen/CGDebugInfo.cpp | 14 +++++++++++++- test/CodeGenCXX/debug-info-scoped-class.cpp | 15 +++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 test/CodeGenCXX/debug-info-scoped-class.cpp diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index 7bd0e11439f..34dcea11266 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -2051,13 +2051,25 @@ llvm::DIType *CGDebugInfo::CreateEnumType(const EnumType *Ty) { // If this is just a forward declaration, construct an appropriately // marked node and just return it. if (isImportedFromModule || !ED->getDefinition()) { - llvm::DIScope *EDContext = getDeclContextDescriptor(ED); llvm::DIFile *DefUnit = getOrCreateFile(ED->getLocation()); + + // It is possible for enums to be created as part of their own + // declcontext. We need to cache a placeholder to avoid the type being + // created twice before hitting the cache. + llvm::DIScope *EDContext = DBuilder.createReplaceableCompositeType( + llvm::dwarf::DW_TAG_enumeration_type, "", TheCU, DefUnit, 0); + unsigned Line = getLineNumber(ED->getLocation()); StringRef EDName = ED->getName(); llvm::DIType *RetTy = DBuilder.createReplaceableCompositeType( llvm::dwarf::DW_TAG_enumeration_type, EDName, EDContext, DefUnit, Line, 0, Size, Align, llvm::DINode::FlagFwdDecl, FullName); + + // Cache the enum type so it is available when building the declcontext + // and replace the declcontect with the real thing. + TypeCache[Ty].reset(RetTy); + EDContext->replaceAllUsesWith(getDeclContextDescriptor(ED)); + ReplaceMap.emplace_back( std::piecewise_construct, std::make_tuple(Ty), std::make_tuple(static_cast(RetTy))); diff --git a/test/CodeGenCXX/debug-info-scoped-class.cpp b/test/CodeGenCXX/debug-info-scoped-class.cpp new file mode 100644 index 00000000000..de4aee9a1b4 --- /dev/null +++ b/test/CodeGenCXX/debug-info-scoped-class.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -emit-llvm -debug-info-kind=standalone -std=c++11 \ +// RUN: -triple thumbv7-apple-ios %s -o - | FileCheck %s + +// This forward-declared scoped enum will be created while building its own +// declcontext. Make sure it is only emitted once. + +struct A { + enum class Return; + Return f1(); +}; +A::Return* f2() {} + +// CHECK: !DICompositeType(tag: DW_TAG_enumeration_type, name: "Return", +// CHECK-SAME: flags: DIFlagFwdDecl, +// CHECK-NOT: tag: DW_TAG_enumeration_type, name: "Return" From ef8307b88c8eefc7d500ead898dbafd7a4ebef82 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Sat, 6 Feb 2016 18:39:34 +0000 Subject: [PATCH 133/742] Add a missing call to MDNode::deleteTemporary(). Follow-up to r259975. Kudos to the ASAN bots! git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260002 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 4404328870f6e3758e5d4d357442b39298f4e8f7) --- lib/CodeGen/CGDebugInfo.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index 34dcea11266..3da0765d375 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -2056,19 +2056,20 @@ llvm::DIType *CGDebugInfo::CreateEnumType(const EnumType *Ty) { // It is possible for enums to be created as part of their own // declcontext. We need to cache a placeholder to avoid the type being // created twice before hitting the cache. - llvm::DIScope *EDContext = DBuilder.createReplaceableCompositeType( + llvm::DIScope *TmpContext = DBuilder.createReplaceableCompositeType( llvm::dwarf::DW_TAG_enumeration_type, "", TheCU, DefUnit, 0); unsigned Line = getLineNumber(ED->getLocation()); StringRef EDName = ED->getName(); llvm::DIType *RetTy = DBuilder.createReplaceableCompositeType( - llvm::dwarf::DW_TAG_enumeration_type, EDName, EDContext, DefUnit, Line, + llvm::dwarf::DW_TAG_enumeration_type, EDName, TmpContext, DefUnit, Line, 0, Size, Align, llvm::DINode::FlagFwdDecl, FullName); // Cache the enum type so it is available when building the declcontext // and replace the declcontect with the real thing. TypeCache[Ty].reset(RetTy); - EDContext->replaceAllUsesWith(getDeclContextDescriptor(ED)); + TmpContext->replaceAllUsesWith(getDeclContextDescriptor(ED)); + llvm::MDNode::deleteTemporary(TmpContext); ReplaceMap.emplace_back( std::piecewise_construct, std::make_tuple(Ty), From b5809943ebc69b1374a062f10c63199cf7ed52ec Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Mon, 8 Feb 2016 17:03:28 +0000 Subject: [PATCH 134/742] Use llvm::TempDIScope instead of manually deleting a temporary MDNode. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260113 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit edfe68550c670419cdbb537f5a262e28764377a8) --- lib/CodeGen/CGDebugInfo.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index 3da0765d375..52bd805b81c 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -2056,20 +2056,19 @@ llvm::DIType *CGDebugInfo::CreateEnumType(const EnumType *Ty) { // It is possible for enums to be created as part of their own // declcontext. We need to cache a placeholder to avoid the type being // created twice before hitting the cache. - llvm::DIScope *TmpContext = DBuilder.createReplaceableCompositeType( - llvm::dwarf::DW_TAG_enumeration_type, "", TheCU, DefUnit, 0); + llvm::TempDIScope TmpContext(DBuilder.createReplaceableCompositeType( + llvm::dwarf::DW_TAG_enumeration_type, "", TheCU, DefUnit, 0)); unsigned Line = getLineNumber(ED->getLocation()); StringRef EDName = ED->getName(); llvm::DIType *RetTy = DBuilder.createReplaceableCompositeType( - llvm::dwarf::DW_TAG_enumeration_type, EDName, TmpContext, DefUnit, Line, - 0, Size, Align, llvm::DINode::FlagFwdDecl, FullName); + llvm::dwarf::DW_TAG_enumeration_type, EDName, TmpContext.get(), DefUnit, + Line, 0, Size, Align, llvm::DINode::FlagFwdDecl, FullName); // Cache the enum type so it is available when building the declcontext // and replace the declcontect with the real thing. TypeCache[Ty].reset(RetTy); TmpContext->replaceAllUsesWith(getDeclContextDescriptor(ED)); - llvm::MDNode::deleteTemporary(TmpContext); ReplaceMap.emplace_back( std::piecewise_construct, std::make_tuple(Ty), From 24f89e9b90ce730eeda22e12cee3cfef46b167c7 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Sat, 30 Jan 2016 01:51:20 +0000 Subject: [PATCH 135/742] [SemaCXX] Fix crash-on-invalid while trying to deduce return type of a lambda. rdar://22032373 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259287 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/SemaStmt.cpp | 9 +++++---- test/SemaCXX/lambda-expressions.cpp | 11 +++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index e1b1a47e182..d73441646bb 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -3066,22 +3066,23 @@ bool Sema::DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD, // has multiple return statements, the return type is deduced for each return // statement. [...] if the type deduced is not the same in each deduction, // the program is ill-formed. - if (AT->isDeduced() && !FD->isInvalidDecl()) { + QualType DeducedT = AT->getDeducedType(); + if (!DeducedT.isNull() && !FD->isInvalidDecl()) { AutoType *NewAT = Deduced->getContainedAutoType(); CanQualType OldDeducedType = Context.getCanonicalFunctionResultType( - AT->getDeducedType()); + DeducedT); CanQualType NewDeducedType = Context.getCanonicalFunctionResultType( NewAT->getDeducedType()); if (!FD->isDependentContext() && OldDeducedType != NewDeducedType) { const LambdaScopeInfo *LambdaSI = getCurLambda(); if (LambdaSI && LambdaSI->HasImplicitReturnType) { Diag(ReturnLoc, diag::err_typecheck_missing_return_type_incompatible) - << NewAT->getDeducedType() << AT->getDeducedType() + << NewAT->getDeducedType() << DeducedT << true /*IsLambda*/; } else { Diag(ReturnLoc, diag::err_auto_fn_different_deductions) << (AT->isDecltypeAuto() ? 1 : 0) - << NewAT->getDeducedType() << AT->getDeducedType(); + << NewAT->getDeducedType() << DeducedT; } return true; } diff --git a/test/SemaCXX/lambda-expressions.cpp b/test/SemaCXX/lambda-expressions.cpp index 72adcdbce2f..08446a0ee43 100644 --- a/test/SemaCXX/lambda-expressions.cpp +++ b/test/SemaCXX/lambda-expressions.cpp @@ -476,3 +476,14 @@ int main() { A a; } + +// rdar://22032373 +namespace rdar22032373 { +void foo() { + auto blk = [](bool b) { + if (b) + return undeclared_error; // expected-error {{use of undeclared identifier}} + return 0; + }; +} +} From ad640233df66fb514b8d94ace48d145ff65106e6 Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Thu, 11 Feb 2016 19:19:18 +0000 Subject: [PATCH 136/742] [PR26550] Use a different TBAA root for C++ vs C. This commit changes the root from "Simple C/C++ TBAA" to "Simple C++ TBAA" for C++. The problem is that the type name in the TBAA nodes is generated differently for C vs C++. If we link an IR file for C with an IR file for C++, since they have the same root and the type names are different, accesses to the two type nodes will be considered no-alias, even though the two type nodes are from the same type in a header file. The fix is to use different roots for C and C++. Types from C will be treated conservatively in respect to types from C++. Follow-up commits will change the C root to "Simple C TBAA" plus some mangling change for C types to make it a little more aggresive. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260567 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CodeGenTBAA.cpp | 8 ++++++-- test/CodeGen/tbaa-class.cpp | 2 +- test/CodeGen/tbaa-for-vptr.cpp | 2 +- test/CodeGen/tbaa.cpp | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/CodeGen/CodeGenTBAA.cpp b/lib/CodeGen/CodeGenTBAA.cpp index c3c925cde2f..04224e72679 100644 --- a/lib/CodeGen/CodeGenTBAA.cpp +++ b/lib/CodeGen/CodeGenTBAA.cpp @@ -44,8 +44,12 @@ llvm::MDNode *CodeGenTBAA::getRoot() { // if our LLVM IR is linked with LLVM IR from a different front-end // (or a different version of this front-end), their TBAA trees will // remain distinct, and the optimizer will treat them conservatively. - if (!Root) - Root = MDHelper.createTBAARoot("Simple C/C++ TBAA"); + if (!Root) { + if (Features.CPlusPlus) + Root = MDHelper.createTBAARoot("Simple C++ TBAA"); + else + Root = MDHelper.createTBAARoot("Simple C/C++ TBAA"); + } return Root; } diff --git a/test/CodeGen/tbaa-class.cpp b/test/CodeGen/tbaa-class.cpp index f611ae5abb8..7172e05d9e3 100644 --- a/test/CodeGen/tbaa-class.cpp +++ b/test/CodeGen/tbaa-class.cpp @@ -199,7 +199,7 @@ uint32_t g12(StructC *C, StructD *D, uint64_t count) { } // CHECK: [[TYPE_char:!.*]] = !{!"omnipotent char", [[TAG_cxx_tbaa:!.*]], -// CHECK: [[TAG_cxx_tbaa]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[TAG_cxx_tbaa]] = !{!"Simple C++ TBAA"} // CHECK: [[TAG_i32]] = !{[[TYPE_i32:!.*]], [[TYPE_i32]], i64 0} // CHECK: [[TYPE_i32]] = !{!"int", [[TYPE_char]], // CHECK: [[TAG_i16]] = !{[[TYPE_i16:!.*]], [[TYPE_i16]], i64 0} diff --git a/test/CodeGen/tbaa-for-vptr.cpp b/test/CodeGen/tbaa-for-vptr.cpp index 35e95a54dab..7b8ae2099e4 100644 --- a/test/CodeGen/tbaa-for-vptr.cpp +++ b/test/CodeGen/tbaa-for-vptr.cpp @@ -32,4 +32,4 @@ void CallFoo(A *a, int (A::*fp)() const) { // // CHECK: [[NUM]] = !{[[TYPE:!.*]], [[TYPE]], i64 0} // CHECK: [[TYPE]] = !{!"vtable pointer", !{{.*}} -// NOTBAA-NOT: = !{!"Simple C/C++ TBAA"} +// NOTBAA-NOT: = !{!"Simple C++ TBAA"} diff --git a/test/CodeGen/tbaa.cpp b/test/CodeGen/tbaa.cpp index c43ca58bc3f..f98c46f1497 100644 --- a/test/CodeGen/tbaa.cpp +++ b/test/CodeGen/tbaa.cpp @@ -237,7 +237,7 @@ uint32_t g15(StructS *S, StructS3 *S3, uint64_t count) { } // CHECK: [[TYPE_char:!.*]] = !{!"omnipotent char", [[TAG_cxx_tbaa:!.*]], -// CHECK: [[TAG_cxx_tbaa]] = !{!"Simple C/C++ TBAA"} +// CHECK: [[TAG_cxx_tbaa]] = !{!"Simple C++ TBAA"} // CHECK: [[TAG_i32]] = !{[[TYPE_i32:!.*]], [[TYPE_i32]], i64 0} // CHECK: [[TYPE_i32]] = !{!"int", [[TYPE_char]], // CHECK: [[TAG_i16]] = !{[[TYPE_i16:!.*]], [[TYPE_i16]], i64 0} From 12c6bacf6f122290be3edbb3d7a5fe858b7a96a9 Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Thu, 4 Feb 2016 20:05:40 +0000 Subject: [PATCH 137/742] Fix a crash when there is a typo in the return statement. If the typo happens after a successful deduction for an earlier return statement, we should check if the deduced type is null before using it. The typo correction happens after we try to deduce the return type and we ignore the deduction from the typo and continue to typo correction. rdar://24342247 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259820 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/SemaStmt.cpp | 5 +++++ test/SemaCXX/typo-correction-crash.cpp | 11 +++++++++++ 2 files changed, 16 insertions(+) create mode 100644 test/SemaCXX/typo-correction-crash.cpp diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index d73441646bb..836073a680c 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -3069,6 +3069,11 @@ bool Sema::DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD, QualType DeducedT = AT->getDeducedType(); if (!DeducedT.isNull() && !FD->isInvalidDecl()) { AutoType *NewAT = Deduced->getContainedAutoType(); + // It is possible that NewAT->getDeducedType() is null. When that happens, + // we should not crash, instead we ignore this deduction. + if (NewAT->getDeducedType().isNull()) + return false; + CanQualType OldDeducedType = Context.getCanonicalFunctionResultType( DeducedT); CanQualType NewDeducedType = Context.getCanonicalFunctionResultType( diff --git a/test/SemaCXX/typo-correction-crash.cpp b/test/SemaCXX/typo-correction-crash.cpp new file mode 100644 index 00000000000..f01facd603e --- /dev/null +++ b/test/SemaCXX/typo-correction-crash.cpp @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++14 -verify %s +auto check1() { + return 1; + return s; // expected-error {{use of undeclared identifier 's'}} +} + +int test = 11; // expected-note {{'test' declared here}} +auto check2() { + return "s"; + return tes; // expected-error {{use of undeclared identifier 'tes'; did you mean 'test'?}} +} From 3fe7d68584212c3268384543641a91159bb1f28b Mon Sep 17 00:00:00 2001 From: Chris Bieneman Date: Fri, 12 Feb 2016 21:36:55 +0000 Subject: [PATCH 138/742] [CMake] Improve the clang order-file generation workflow Summary: This commit re-lands r259862. The underlying cause of the build breakage was an incorrectly written capabilities test. In tools/Driver/CMakeLists.txt I was attempting to check if a linker flag worked, the test was passing it to the compiler, not the linker. CMake doesn't have a linker test, so we have a hand-rolled one. Original Patch Review: http://reviews.llvm.org/D16896 Original Summary: With this change generating clang order files using dtrace uses the following workflow: cmake ninja generate-order-file ninja clang This patch works by setting a default path to the order file (which can be overridden by the user). If the order file doesn't exist during configuration CMake will create an empty one. CMake then ties up the dependencies between the clang link job and the order file, and generate-order-file overwrites CLANG_ORDER_FILE with the new order file. Reviewers: bogner Subscribers: cfe-commits Differential Revision: http://reviews.llvm.org/D16999 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260742 91177308-0d34-0410-b5e6-96231b3b80d8 --- CMakeLists.txt | 9 +++++++-- tools/driver/CMakeLists.txt | 22 +++++++++++++++++----- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bb6194385b5..61ea154391b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -595,8 +595,13 @@ endif() set(CLANG_ORDER_FILE ${CMAKE_CURRENT_BINARY_DIR}/clang.order CACHE FILEPATH "Order file to use when compiling clang in order to improve startup time.") -if(NOT EXISTS ${CLANG_ORDER_FILE}) - execute_process(COMMAND ${CMAKE_COMMAND} -E touch ${CLANG_ORDER_FILE}) +if(CLANG_ORDER_FILE AND NOT EXISTS ${CLANG_ORDER_FILE}) + string(FIND CLANG_ORDER_FILE "${CMAKE_CURRENT_BINARY_DIR}" PATH_START) + if(PATH_START EQUAL 0) + file(WRITE ${CLANG_ORDER_FILE} "\n") + else() + message(FATAL_ERROR "Specified order file '${CLANG_ORDER_FILE}' does not exist.") + endif() endif() if (CLANG_BUILT_STANDALONE OR CMAKE_VERSION VERSION_EQUAL 3 OR diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt index 36457340569..dc6cd4db86d 100644 --- a/tools/driver/CMakeLists.txt +++ b/tools/driver/CMakeLists.txt @@ -89,12 +89,24 @@ if (APPLE) set(TOOL_INFO_BUILD_VERSION) endif() -check_cxx_compiler_flag("-Wl,-order_file,${CLANG_ORDER_FILE}" - LINKER_HAS_ORDER_FILE_FLAG) +if(CLANG_ORDER_FILE) + include(CMakePushCheckState) -if(LINKER_HAS_ORDER_FILE_FLAG AND CLANG_ORDER_FILE) - target_link_libraries(clang "-Wl,-order_file,${CLANG_ORDER_FILE}") - set_target_properties(clang PROPERTIES LINK_DEPENDS ${CLANG_ORDER_FILE}) + function(check_linker_flag flag out_var) + cmake_push_check_state() + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${flag}") + check_cxx_compiler_flag("" ${out_var}) + cmake_pop_check_state() + endfunction() + + # This is a test to ensure the actual order file works with the linker. + check_linker_flag("-Wl,-order_file,${CLANG_ORDER_FILE}" + LINKER_ORDER_FILE_WORKS) + + if(LINKER_ORDER_FILE_WORKS) + target_link_libraries(clang "-Wl,-order_file,${CLANG_ORDER_FILE}") + set_target_properties(clang PROPERTIES LINK_DEPENDS ${CLANG_ORDER_FILE}) + endif() endif() if(WITH_POLLY AND LINK_POLLY_INTO_TOOLS) From fd387d8a85b1d03a6715aad58c6edf5743b95618 Mon Sep 17 00:00:00 2001 From: Chris Bieneman Date: Fri, 12 Feb 2016 21:46:25 +0000 Subject: [PATCH 139/742] [CMake] Fixing bots I broke. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260744 91177308-0d34-0410-b5e6-96231b3b80d8 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 61ea154391b..8bf9e26e3cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -596,7 +596,7 @@ set(CLANG_ORDER_FILE ${CMAKE_CURRENT_BINARY_DIR}/clang.order CACHE FILEPATH "Order file to use when compiling clang in order to improve startup time.") if(CLANG_ORDER_FILE AND NOT EXISTS ${CLANG_ORDER_FILE}) - string(FIND CLANG_ORDER_FILE "${CMAKE_CURRENT_BINARY_DIR}" PATH_START) + string(FIND "${CLANG_ORDER_FILE}" "${CMAKE_CURRENT_BINARY_DIR}" PATH_START) if(PATH_START EQUAL 0) file(WRITE ${CLANG_ORDER_FILE} "\n") else() From 2b99e2cd3c09e4a57be0ef2a01cfc8426e949f29 Mon Sep 17 00:00:00 2001 From: Chris Bieneman Date: Fri, 12 Feb 2016 21:36:55 +0000 Subject: [PATCH 140/742] [CMake] Improve the clang order-file generation workflow Summary: This commit re-lands r259862. The underlying cause of the build breakage was an incorrectly written capabilities test. In tools/Driver/CMakeLists.txt I was attempting to check if a linker flag worked, the test was passing it to the compiler, not the linker. CMake doesn't have a linker test, so we have a hand-rolled one. Original Patch Review: http://reviews.llvm.org/D16896 Original Summary: With this change generating clang order files using dtrace uses the following workflow: cmake ninja generate-order-file ninja clang This patch works by setting a default path to the order file (which can be overridden by the user). If the order file doesn't exist during configuration CMake will create an empty one. CMake then ties up the dependencies between the clang link job and the order file, and generate-order-file overwrites CLANG_ORDER_FILE with the new order file. Reviewers: bogner Subscribers: cfe-commits Differential Revision: http://reviews.llvm.org/D16999 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260742 91177308-0d34-0410-b5e6-96231b3b80d8 --- CMakeLists.txt | 9 +++++++-- tools/driver/CMakeLists.txt | 22 +++++++++++++++++----- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bb6194385b5..61ea154391b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -595,8 +595,13 @@ endif() set(CLANG_ORDER_FILE ${CMAKE_CURRENT_BINARY_DIR}/clang.order CACHE FILEPATH "Order file to use when compiling clang in order to improve startup time.") -if(NOT EXISTS ${CLANG_ORDER_FILE}) - execute_process(COMMAND ${CMAKE_COMMAND} -E touch ${CLANG_ORDER_FILE}) +if(CLANG_ORDER_FILE AND NOT EXISTS ${CLANG_ORDER_FILE}) + string(FIND CLANG_ORDER_FILE "${CMAKE_CURRENT_BINARY_DIR}" PATH_START) + if(PATH_START EQUAL 0) + file(WRITE ${CLANG_ORDER_FILE} "\n") + else() + message(FATAL_ERROR "Specified order file '${CLANG_ORDER_FILE}' does not exist.") + endif() endif() if (CLANG_BUILT_STANDALONE OR CMAKE_VERSION VERSION_EQUAL 3 OR diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt index 36457340569..dc6cd4db86d 100644 --- a/tools/driver/CMakeLists.txt +++ b/tools/driver/CMakeLists.txt @@ -89,12 +89,24 @@ if (APPLE) set(TOOL_INFO_BUILD_VERSION) endif() -check_cxx_compiler_flag("-Wl,-order_file,${CLANG_ORDER_FILE}" - LINKER_HAS_ORDER_FILE_FLAG) +if(CLANG_ORDER_FILE) + include(CMakePushCheckState) -if(LINKER_HAS_ORDER_FILE_FLAG AND CLANG_ORDER_FILE) - target_link_libraries(clang "-Wl,-order_file,${CLANG_ORDER_FILE}") - set_target_properties(clang PROPERTIES LINK_DEPENDS ${CLANG_ORDER_FILE}) + function(check_linker_flag flag out_var) + cmake_push_check_state() + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${flag}") + check_cxx_compiler_flag("" ${out_var}) + cmake_pop_check_state() + endfunction() + + # This is a test to ensure the actual order file works with the linker. + check_linker_flag("-Wl,-order_file,${CLANG_ORDER_FILE}" + LINKER_ORDER_FILE_WORKS) + + if(LINKER_ORDER_FILE_WORKS) + target_link_libraries(clang "-Wl,-order_file,${CLANG_ORDER_FILE}") + set_target_properties(clang PROPERTIES LINK_DEPENDS ${CLANG_ORDER_FILE}) + endif() endif() if(WITH_POLLY AND LINK_POLLY_INTO_TOOLS) From 8fc756a91c459c4108100116bbce5d5885989514 Mon Sep 17 00:00:00 2001 From: Chris Bieneman Date: Fri, 12 Feb 2016 21:46:25 +0000 Subject: [PATCH 141/742] [CMake] Fixing bots I broke. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260744 91177308-0d34-0410-b5e6-96231b3b80d8 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 61ea154391b..8bf9e26e3cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -596,7 +596,7 @@ set(CLANG_ORDER_FILE ${CMAKE_CURRENT_BINARY_DIR}/clang.order CACHE FILEPATH "Order file to use when compiling clang in order to improve startup time.") if(CLANG_ORDER_FILE AND NOT EXISTS ${CLANG_ORDER_FILE}) - string(FIND CLANG_ORDER_FILE "${CMAKE_CURRENT_BINARY_DIR}" PATH_START) + string(FIND "${CLANG_ORDER_FILE}" "${CMAKE_CURRENT_BINARY_DIR}" PATH_START) if(PATH_START EQUAL 0) file(WRITE ${CLANG_ORDER_FILE} "\n") else() From 85d25d840df8a3a023a42947c5985febd97dbc05 Mon Sep 17 00:00:00 2001 From: George Burgess IV Date: Thu, 28 Jan 2016 01:38:18 +0000 Subject: [PATCH 142/742] [Sema] Make extended vectors of `bool` an error. In OpenCL, `bool` vectors are a reserved type, and are therefore illegal. Outside of OpenCL, if we try to make an extended vector of N `bool`s, Clang will lower it to an `[N x i1]`. LLVM has no ABI for bitvectors, so lots of operations on such vectors are thoroughly broken. As a result, this patch makes them illegal in everything else, as well. :) Differential Revision: http://reviews.llvm.org/D15721 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259011 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/SemaType.cpp | 14 ++++++++++---- test/CodeGen/convertvector.c | 22 ---------------------- test/Sema/ext_vector_casts.c | 2 ++ test/SemaOpenCL/bool-vectors.cl | 3 +++ 4 files changed, 15 insertions(+), 26 deletions(-) create mode 100644 test/SemaOpenCL/bool-vectors.cl diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 844be540e0a..45564fe6b8c 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -2184,10 +2184,16 @@ QualType Sema::BuildArrayType(QualType T, ArrayType::ArraySizeModifier ASM, /// Run the required checks for the extended vector type. QualType Sema::BuildExtVectorType(QualType T, Expr *ArraySize, SourceLocation AttrLoc) { - // unlike gcc's vector_size attribute, we do not allow vectors to be defined + // Unlike gcc's vector_size attribute, we do not allow vectors to be defined // in conjunction with complex types (pointers, arrays, functions, etc.). - if (!T->isDependentType() && - !T->isIntegerType() && !T->isRealFloatingType()) { + // + // Additionally, OpenCL prohibits vectors of booleans (they're considered a + // reserved data type under OpenCL v2.0 s6.1.4), we don't support selects + // on bitvectors, and we have no well-defined ABI for bitvectors, so vectors + // of bool aren't allowed. + if ((!T->isDependentType() && !T->isIntegerType() && + !T->isRealFloatingType()) || + T->isBooleanType()) { Diag(AttrLoc, diag::err_attribute_invalid_vector_type) << T; return QualType(); } @@ -2201,7 +2207,7 @@ QualType Sema::BuildExtVectorType(QualType T, Expr *ArraySize, return QualType(); } - // unlike gcc's vector_size attribute, the size is specified as the + // Unlike gcc's vector_size attribute, the size is specified as the // number of elements, not the number of bytes. unsigned vectorSize = static_cast(vecSize.getZExtValue()); diff --git a/test/CodeGen/convertvector.c b/test/CodeGen/convertvector.c index 2b23dd96e1b..a534b85a423 100644 --- a/test/CodeGen/convertvector.c +++ b/test/CodeGen/convertvector.c @@ -8,14 +8,6 @@ typedef short vector8short __attribute__((__vector_size__(16))); typedef unsigned long vector8ulong __attribute__((__vector_size__(64))); typedef unsigned short vector8ushort __attribute__((__vector_size__(16))); -#ifdef __cplusplus -#define BOOL bool -#else -#define BOOL _Bool -#endif - -typedef BOOL vector8bool __attribute__((__ext_vector_type__(8))); - #ifdef __cplusplus extern "C" { #endif @@ -32,13 +24,6 @@ vector8double flt_ext(vector8float x) { // CHECK: fpext <8 x float> %{{[^ ]}} to <8 x double> } -vector8bool flt_tobool(vector8float x) { - return __builtin_convertvector(x, vector8bool); - // CHECK-LABEL: @flt_tobool - // CHECK-NOT: fptoui <8 x float> %{{[^ ]}} to <8 x i1> - // CHECK: fcmp une <8 x float> %{{[^ ]}}, zeroinitializer -} - vector8long flt_tosi(vector8float x) { return __builtin_convertvector(x, vector8long); // CHECK-LABEL: @flt_tosi @@ -69,13 +54,6 @@ vector8long int_sext(vector8short x) { // CHECK: sext <8 x i16> %{{[^ ]}} to <8 x i64> } -vector8bool int_tobool(vector8short x) { - return __builtin_convertvector(x, vector8bool); - // CHECK-LABEL: @int_tobool - // CHECK-NOT: trunc <8 x i16> %{{[^ ]}} to <8 x i1> - // CHECK: icmp ne <8 x i16> %{{[^ ]}}, zeroinitializer -} - vector8float int_tofp(vector8short x) { return __builtin_convertvector(x, vector8float); // CHECK-LABEL: @int_tofp diff --git a/test/Sema/ext_vector_casts.c b/test/Sema/ext_vector_casts.c index 924013aeb29..6aaedbe7fd1 100644 --- a/test/Sema/ext_vector_casts.c +++ b/test/Sema/ext_vector_casts.c @@ -1,5 +1,7 @@ // RUN: %clang_cc1 -triple x86_64-apple-macos10.7.0 -fsyntax-only -verify -fno-lax-vector-conversions -Wconversion %s +typedef __attribute__((ext_vector_type(8))) _Bool BoolVector; // expected-error {{invalid vector element type '_Bool'}} + typedef __attribute__(( ext_vector_type(2) )) float float2; typedef __attribute__(( ext_vector_type(3) )) float float3; typedef __attribute__(( ext_vector_type(4) )) int int4; diff --git a/test/SemaOpenCL/bool-vectors.cl b/test/SemaOpenCL/bool-vectors.cl new file mode 100644 index 00000000000..6df4d565300 --- /dev/null +++ b/test/SemaOpenCL/bool-vectors.cl @@ -0,0 +1,3 @@ +// RUN: %clang_cc1 %s -verify -pedantic -fsyntax-only + +typedef __attribute__((ext_vector_type(16))) _Bool bool8; // expected-error{{invalid vector element type 'bool'}} From 9eaa564e6d63851a5e13d43310849ddafb8d02f7 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Fri, 12 Feb 2016 23:10:59 +0000 Subject: [PATCH 143/742] [libclang] Separate the underlying indexing functionality of libclang and introduce it into the clangIndex library. It is a general goodness for libclang itself to mostly be a wrapper of functionality provided by the libraries. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260760 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Index/IndexDataConsumer.h | 61 ++ include/clang/Index/IndexSymbol.h | 112 ++++ include/clang/Index/IndexingAction.h | 47 ++ lib/Index/CMakeLists.txt | 8 + lib/Index/IndexBody.cpp | 347 +++++++++++ {tools/libclang => lib/Index}/IndexDecl.cpp | 272 ++++++--- lib/Index/IndexSymbol.cpp | 187 ++++++ .../Index}/IndexTypeSourceInfo.cpp | 98 ++- lib/Index/IndexingAction.cpp | 140 +++++ lib/Index/IndexingContext.cpp | 325 ++++++++++ lib/Index/IndexingContext.h | 121 ++++ tools/libclang/CMakeLists.txt | 5 +- ...ingContext.cpp => CXIndexDataConsumer.cpp} | 557 +++++++++++------- ...ndexingContext.h => CXIndexDataConsumer.h} | 45 +- tools/libclang/IndexBody.cpp | 224 ------- tools/libclang/Indexing.cpp | 168 ++---- 16 files changed, 2023 insertions(+), 694 deletions(-) create mode 100644 include/clang/Index/IndexDataConsumer.h create mode 100644 include/clang/Index/IndexSymbol.h create mode 100644 include/clang/Index/IndexingAction.h create mode 100644 lib/Index/IndexBody.cpp rename {tools/libclang => lib/Index}/IndexDecl.cpp (50%) create mode 100644 lib/Index/IndexSymbol.cpp rename {tools/libclang => lib/Index}/IndexTypeSourceInfo.cpp (52%) create mode 100644 lib/Index/IndexingAction.cpp create mode 100644 lib/Index/IndexingContext.cpp create mode 100644 lib/Index/IndexingContext.h rename tools/libclang/{IndexingContext.cpp => CXIndexDataConsumer.cpp} (70%) rename tools/libclang/{IndexingContext.h => CXIndexDataConsumer.h} (91%) delete mode 100644 tools/libclang/IndexBody.cpp diff --git a/include/clang/Index/IndexDataConsumer.h b/include/clang/Index/IndexDataConsumer.h new file mode 100644 index 00000000000..cb00345f56b --- /dev/null +++ b/include/clang/Index/IndexDataConsumer.h @@ -0,0 +1,61 @@ +//===--- IndexDataConsumer.h - Abstract index data consumer ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXDATACONSUMER_H +#define LLVM_CLANG_INDEX_INDEXDATACONSUMER_H + +#include "clang/Index/IndexSymbol.h" + +namespace clang { + class DeclContext; + class Expr; + class FileID; + class IdentifierInfo; + class ImportDecl; + class MacroInfo; + +namespace index { + +class IndexDataConsumer { +public: + struct ASTNodeInfo { + const Expr *OrigE; + const Decl *OrigD; + const Decl *Parent; + const DeclContext *ContainerDC; + }; + + virtual ~IndexDataConsumer() {} + + /// \returns true to continue indexing, or false to abort. + virtual bool handleDeclOccurence(const Decl *D, SymbolRoleSet Roles, + ArrayRef Relations, + FileID FID, unsigned Offset, + ASTNodeInfo ASTNode); + + /// \returns true to continue indexing, or false to abort. + virtual bool handleMacroOccurence(const IdentifierInfo *Name, + const MacroInfo *MI, SymbolRoleSet Roles, + FileID FID, unsigned Offset); + + /// \returns true to continue indexing, or false to abort. + virtual bool handleModuleOccurence(const ImportDecl *ImportD, + SymbolRoleSet Roles, + FileID FID, unsigned Offset); + + virtual void finish() {} + +private: + virtual void _anchor(); +}; + +} // namespace index +} // namespace clang + +#endif diff --git a/include/clang/Index/IndexSymbol.h b/include/clang/Index/IndexSymbol.h new file mode 100644 index 00000000000..0849241b60e --- /dev/null +++ b/include/clang/Index/IndexSymbol.h @@ -0,0 +1,112 @@ +//===--- IndexSymbol.h - Types and functions for indexing symbols ---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXSYMBOL_H +#define LLVM_CLANG_INDEX_INDEXSYMBOL_H + +#include "clang/Basic/LLVM.h" + +namespace clang { + class Decl; + +namespace index { + +enum class SymbolKind : uint8_t { + Unknown, + + Module, + Macro, + + Enum, + Struct, + Union, + Typedef, + + Function, + Variable, + Field, + EnumConstant, + + ObjCClass, + ObjCProtocol, + ObjCCategory, + + ObjCInstanceMethod, + ObjCClassMethod, + ObjCProperty, + ObjCIvar, + + CXXClass, + CXXNamespace, + CXXNamespaceAlias, + CXXStaticVariable, + CXXStaticMethod, + CXXInstanceMethod, + CXXConstructor, + CXXDestructor, + CXXConversionFunction, + CXXTypeAlias, + CXXInterface, +}; + +enum class SymbolLanguage { + C, + ObjC, + CXX, +}; + +enum class SymbolCXXTemplateKind { + NonTemplate, + Template, + TemplatePartialSpecialization, + TemplateSpecialization, +}; + +/// Set of roles that are attributed to symbol occurrences. +enum class SymbolRole : uint16_t { + Declaration = 1 << 0, + Definition = 1 << 1, + Reference = 1 << 2, + Read = 1 << 3, + Write = 1 << 4, + Call = 1 << 5, + Dynamic = 1 << 6, + AddressOf = 1 << 7, + Implicit = 1 << 8, + + // Relation roles. + RelationChildOf = 1 << 9, + RelationBaseOf = 1 << 10, + RelationOverrideOf = 1 << 11, + RelationReceivedBy = 1 << 12, +}; +static const unsigned SymbolRoleBitNum = 13; +typedef unsigned SymbolRoleSet; + +/// Represents a relation to another symbol for a symbol occurrence. +struct SymbolRelation { + SymbolRoleSet Roles; + const Decl *RelatedSymbol; + + SymbolRelation(SymbolRoleSet Roles, const Decl *Sym) + : Roles(Roles), RelatedSymbol(Sym) {} +}; + +struct SymbolInfo { + SymbolKind Kind; + SymbolCXXTemplateKind TemplateKind; + SymbolLanguage Lang; +}; + +SymbolInfo getSymbolInfo(const Decl *D); + +} // namespace index +} // namespace clang + +#endif diff --git a/include/clang/Index/IndexingAction.h b/include/clang/Index/IndexingAction.h new file mode 100644 index 00000000000..dfc363a049d --- /dev/null +++ b/include/clang/Index/IndexingAction.h @@ -0,0 +1,47 @@ +//===--- IndexingAction.h - Frontend index action -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXINGACTION_H +#define LLVM_CLANG_INDEX_INDEXINGACTION_H + +#include "clang/Basic/LLVM.h" +#include + +namespace clang { + class ASTUnit; + class FrontendAction; + +namespace index { + class IndexDataConsumer; + +struct IndexingOptions { + enum class SystemSymbolFilterKind { + None, + DeclarationsOnly, + All, + }; + + SystemSymbolFilterKind SystemSymbolFilter + = SystemSymbolFilterKind::DeclarationsOnly; + bool IndexFunctionLocals = false; +}; + +std::unique_ptr +createIndexingAction(std::unique_ptr WrappedAction, + std::shared_ptr DataConsumer, + IndexingOptions Opts); + +void indexASTUnit(ASTUnit &Unit, + std::shared_ptr DataConsumer, + IndexingOptions Opts); + +} // namespace index +} // namespace clang + +#endif diff --git a/lib/Index/CMakeLists.txt b/lib/Index/CMakeLists.txt index 3869c32c879..cb2b931235e 100644 --- a/lib/Index/CMakeLists.txt +++ b/lib/Index/CMakeLists.txt @@ -5,14 +5,22 @@ set(LLVM_LINK_COMPONENTS add_clang_library(clangIndex CommentToXML.cpp USRGeneration.cpp + IndexBody.cpp + IndexDecl.cpp + IndexingAction.cpp + IndexingContext.cpp + IndexSymbol.cpp + IndexTypeSourceInfo.cpp ADDITIONAL_HEADERS + IndexingContext.h SimpleFormatContext.h LINK_LIBS clangAST clangBasic clangFormat + clangFrontend clangRewrite clangToolingCore ) diff --git a/lib/Index/IndexBody.cpp b/lib/Index/IndexBody.cpp new file mode 100644 index 00000000000..56fba28387d --- /dev/null +++ b/lib/Index/IndexBody.cpp @@ -0,0 +1,347 @@ +//===- IndexBody.cpp - Indexing statements --------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "IndexingContext.h" +#include "clang/AST/RecursiveASTVisitor.h" + +using namespace clang; +using namespace clang::index; + +namespace { + +class BodyIndexer : public RecursiveASTVisitor { + IndexingContext &IndexCtx; + const NamedDecl *Parent; + const DeclContext *ParentDC; + SmallVector StmtStack; + + typedef RecursiveASTVisitor base; +public: + BodyIndexer(IndexingContext &indexCtx, + const NamedDecl *Parent, const DeclContext *DC) + : IndexCtx(indexCtx), Parent(Parent), ParentDC(DC) { } + + bool shouldWalkTypesOfTypeLocs() const { return false; } + + bool TraverseStmt(Stmt *S) { + StmtStack.push_back(S); + bool ret = base::TraverseStmt(S); + StmtStack.pop_back(); + return ret; + } + + bool TraverseTypeLoc(TypeLoc TL) { + IndexCtx.indexTypeLoc(TL, Parent, ParentDC); + return true; + } + + bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) { + IndexCtx.indexNestedNameSpecifierLoc(NNS, Parent, ParentDC); + return true; + } + + SymbolRoleSet getRolesForRef(const Expr *E, + SmallVectorImpl &Relations) { + SymbolRoleSet Roles{}; + assert(!StmtStack.empty() && E == StmtStack.back()); + if (StmtStack.size() == 1) + return Roles; + auto It = StmtStack.end()-2; + while (isa(*It) || isa(*It)) { + if (auto ICE = dyn_cast(*It)) { + if (ICE->getCastKind() == CK_LValueToRValue) + Roles |= (unsigned)(unsigned)SymbolRole::Read; + } + if (It == StmtStack.begin()) + break; + --It; + } + const Stmt *Parent = *It; + + if (auto BO = dyn_cast(Parent)) { + if (BO->getOpcode() == BO_Assign && BO->getLHS()->IgnoreParenCasts() == E) + Roles |= (unsigned)SymbolRole::Write; + + } else if (auto UO = dyn_cast(Parent)) { + if (UO->isIncrementDecrementOp()) { + Roles |= (unsigned)SymbolRole::Read; + Roles |= (unsigned)SymbolRole::Write; + } else if (UO->getOpcode() == UO_AddrOf) { + Roles |= (unsigned)SymbolRole::AddressOf; + } + + } else if (auto CA = dyn_cast(Parent)) { + if (CA->getLHS()->IgnoreParenCasts() == E) { + Roles |= (unsigned)SymbolRole::Read; + Roles |= (unsigned)SymbolRole::Write; + } + + } else if (auto CE = dyn_cast(Parent)) { + if (CE->getCallee()->IgnoreParenCasts() == E) { + Roles |= (unsigned)SymbolRole::Call; + if (auto *ME = dyn_cast(E)) { + if (auto *CXXMD = dyn_cast_or_null(ME->getMemberDecl())) + if (CXXMD->isVirtual() && !ME->hasQualifier()) { + Roles |= (unsigned)SymbolRole::Dynamic; + auto BaseTy = ME->getBase()->IgnoreImpCasts()->getType(); + if (!BaseTy.isNull()) + if (auto *CXXRD = BaseTy->getPointeeCXXRecordDecl()) + Relations.emplace_back((unsigned)SymbolRole::RelationReceivedBy, + CXXRD); + } + } + } else if (auto CXXOp = dyn_cast(CE)) { + if (CXXOp->getNumArgs() > 0 && CXXOp->getArg(0)->IgnoreParenCasts() == E) { + OverloadedOperatorKind Op = CXXOp->getOperator(); + if (Op == OO_Equal) { + Roles |= (unsigned)SymbolRole::Write; + } else if ((Op >= OO_PlusEqual && Op <= OO_PipeEqual) || + Op == OO_LessLessEqual || Op == OO_GreaterGreaterEqual || + Op == OO_PlusPlus || Op == OO_MinusMinus) { + Roles |= (unsigned)SymbolRole::Read; + Roles |= (unsigned)SymbolRole::Write; + } else if (Op == OO_Amp) { + Roles |= (unsigned)SymbolRole::AddressOf; + } + } + } + } + + return Roles; + } + + bool VisitDeclRefExpr(DeclRefExpr *E) { + SmallVector Relations; + SymbolRoleSet Roles = getRolesForRef(E, Relations); + return IndexCtx.handleReference(E->getDecl(), E->getLocation(), + Parent, ParentDC, Roles, Relations, E); + } + + bool VisitMemberExpr(MemberExpr *E) { + SourceLocation Loc = E->getMemberLoc(); + if (Loc.isInvalid()) + Loc = E->getLocStart(); + SmallVector Relations; + SymbolRoleSet Roles = getRolesForRef(E, Relations); + return IndexCtx.handleReference(E->getMemberDecl(), Loc, + Parent, ParentDC, Roles, Relations, E); + } + + bool VisitDesignatedInitExpr(DesignatedInitExpr *E) { + for (DesignatedInitExpr::reverse_designators_iterator + D = E->designators_rbegin(), DEnd = E->designators_rend(); + D != DEnd; ++D) { + if (D->isFieldDesignator()) + return IndexCtx.handleReference(D->getField(), D->getFieldLoc(), + Parent, ParentDC, SymbolRoleSet(), + {}, E); + } + return true; + } + + bool VisitObjCIvarRefExpr(ObjCIvarRefExpr *E) { + SmallVector Relations; + SymbolRoleSet Roles = getRolesForRef(E, Relations); + return IndexCtx.handleReference(E->getDecl(), E->getLocation(), + Parent, ParentDC, Roles, Relations, E); + } + + bool VisitObjCMessageExpr(ObjCMessageExpr *E) { + auto isDynamic = [](const ObjCMessageExpr *MsgE)->bool { + if (MsgE->getReceiverKind() != ObjCMessageExpr::Instance) + return false; + if (auto *RecE = dyn_cast( + MsgE->getInstanceReceiver()->IgnoreParenCasts())) { + if (RecE->getMethodFamily() == OMF_alloc) + return false; + } + return true; + }; + + if (ObjCMethodDecl *MD = E->getMethodDecl()) { + SymbolRoleSet Roles = (unsigned)SymbolRole::Call; + if (E->isImplicit()) + Roles |= (unsigned)SymbolRole::Implicit; + + SmallVector Relations; + if (isDynamic(E)) { + Roles |= (unsigned)SymbolRole::Dynamic; + if (auto *RecD = E->getReceiverInterface()) + Relations.emplace_back((unsigned)SymbolRole::RelationReceivedBy, RecD); + } + + return IndexCtx.handleReference(MD, E->getSelectorStartLoc(), + Parent, ParentDC, Roles, Relations, E); + } + return true; + } + + bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *E) { + if (E->isExplicitProperty()) + return IndexCtx.handleReference(E->getExplicitProperty(), E->getLocation(), + Parent, ParentDC, SymbolRoleSet(), {}, E); + + // No need to do a handleReference for the objc method, because there will + // be a message expr as part of PseudoObjectExpr. + return true; + } + + bool VisitMSPropertyRefExpr(MSPropertyRefExpr *E) { + return IndexCtx.handleReference(E->getPropertyDecl(), E->getMemberLoc(), + Parent, ParentDC, SymbolRoleSet(), {}, E); + } + + bool VisitObjCProtocolExpr(ObjCProtocolExpr *E) { + return IndexCtx.handleReference(E->getProtocol(), E->getProtocolIdLoc(), + Parent, ParentDC, SymbolRoleSet(), {}, E); + } + + bool VisitObjCBoxedExpr(ObjCBoxedExpr *E) { + if (ObjCMethodDecl *MD = E->getBoxingMethod()) { + SymbolRoleSet Roles = (unsigned)SymbolRole::Call; + Roles |= (unsigned)SymbolRole::Implicit; + return IndexCtx.handleReference(MD, E->getLocStart(), + Parent, ParentDC, Roles, {}, E); + } + return true; + } + + bool VisitObjCDictionaryLiteral(ObjCDictionaryLiteral *E) { + if (ObjCMethodDecl *MD = E->getDictWithObjectsMethod()) { + SymbolRoleSet Roles = (unsigned)SymbolRole::Call; + Roles |= (unsigned)SymbolRole::Implicit; + return IndexCtx.handleReference(MD, E->getLocStart(), + Parent, ParentDC, Roles, {}, E); + } + return true; + } + + bool VisitObjCArrayLiteral(ObjCArrayLiteral *E) { + if (ObjCMethodDecl *MD = E->getArrayWithObjectsMethod()) { + SymbolRoleSet Roles = (unsigned)SymbolRole::Call; + Roles |= (unsigned)SymbolRole::Implicit; + return IndexCtx.handleReference(MD, E->getLocStart(), + Parent, ParentDC, Roles, {}, E); + } + return true; + } + + bool VisitCXXConstructExpr(CXXConstructExpr *E) { + return IndexCtx.handleReference(E->getConstructor(), E->getLocation(), + Parent, ParentDC, (unsigned)SymbolRole::Call, {}, E); + } + + bool TraverseCXXOperatorCallExpr(CXXOperatorCallExpr *E, + DataRecursionQueue *Q = nullptr) { + if (E->getOperatorLoc().isInvalid()) + return true; // implicit. + return base::TraverseCXXOperatorCallExpr(E); + } + + bool VisitDeclStmt(DeclStmt *S) { + if (IndexCtx.shouldIndexFunctionLocalSymbols()) { + IndexCtx.indexDeclGroupRef(S->getDeclGroup()); + return true; + } + + DeclGroupRef DG = S->getDeclGroup(); + for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) { + const Decl *D = *I; + if (!D) + continue; + if (!IndexCtx.isFunctionLocalDecl(D)) + IndexCtx.indexTopLevelDecl(D); + } + + return true; + } + + bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C) { + if (C->capturesThis() || C->capturesVLAType()) + return true; + + if (C->capturesVariable() && IndexCtx.shouldIndexFunctionLocalSymbols()) + return IndexCtx.handleReference(C->getCapturedVar(), C->getLocation(), + Parent, ParentDC, SymbolRoleSet()); + + // FIXME: Lambda init-captures. + return true; + } + + // RecursiveASTVisitor visits both syntactic and semantic forms, duplicating + // the things that we visit. Make sure to only visit the semantic form. + // Also visit things that are in the syntactic form but not the semantic one, + // for example the indices in DesignatedInitExprs. + bool TraverseInitListExpr(InitListExpr *S, DataRecursionQueue *Q = nullptr) { + + class SyntacticFormIndexer : + public RecursiveASTVisitor { + IndexingContext &IndexCtx; + const NamedDecl *Parent; + const DeclContext *ParentDC; + + public: + SyntacticFormIndexer(IndexingContext &indexCtx, + const NamedDecl *Parent, const DeclContext *DC) + : IndexCtx(indexCtx), Parent(Parent), ParentDC(DC) { } + + bool shouldWalkTypesOfTypeLocs() const { return false; } + + bool VisitDesignatedInitExpr(DesignatedInitExpr *E) { + for (DesignatedInitExpr::reverse_designators_iterator + D = E->designators_rbegin(), DEnd = E->designators_rend(); + D != DEnd; ++D) { + if (D->isFieldDesignator()) + return IndexCtx.handleReference(D->getField(), D->getFieldLoc(), + Parent, ParentDC, SymbolRoleSet(), + {}, E); + } + return true; + } + }; + + auto visitForm = [&](InitListExpr *Form) { + for (Stmt *SubStmt : Form->children()) { + if (!TraverseStmt(SubStmt)) + return false; + } + return true; + }; + + InitListExpr *SemaForm = S->isSemanticForm() ? S : S->getSemanticForm(); + InitListExpr *SyntaxForm = S->isSemanticForm() ? S->getSyntacticForm() : S; + + if (SemaForm) { + // Visit things present in syntactic form but not the semantic form. + if (SyntaxForm) { + SyntacticFormIndexer(IndexCtx, Parent, ParentDC).TraverseStmt(SyntaxForm); + } + return visitForm(SemaForm); + } + + // No semantic, try the syntactic. + if (SyntaxForm) { + return visitForm(SyntaxForm); + } + + return true; + } +}; + +} // anonymous namespace + +void IndexingContext::indexBody(const Stmt *S, const NamedDecl *Parent, + const DeclContext *DC) { + if (!S) + return; + + if (!DC) + DC = Parent->getLexicalDeclContext(); + BodyIndexer(*this, Parent, DC).TraverseStmt(const_cast(S)); +} diff --git a/tools/libclang/IndexDecl.cpp b/lib/Index/IndexDecl.cpp similarity index 50% rename from tools/libclang/IndexDecl.cpp rename to lib/Index/IndexDecl.cpp index aa97129c23f..76f68e564c9 100644 --- a/tools/libclang/IndexDecl.cpp +++ b/lib/Index/IndexDecl.cpp @@ -1,4 +1,4 @@ -//===- CIndexHigh.cpp - Higher level API functions ------------------------===// +//===- IndexDecl.cpp - Indexing declarations ------------------------------===// // // The LLVM Compiler Infrastructure // @@ -8,10 +8,11 @@ //===----------------------------------------------------------------------===// #include "IndexingContext.h" +#include "clang/Index/IndexDataConsumer.h" #include "clang/AST/DeclVisitor.h" using namespace clang; -using namespace cxindex; +using namespace index; namespace { @@ -22,6 +23,13 @@ class IndexingDeclVisitor : public ConstDeclVisitor { explicit IndexingDeclVisitor(IndexingContext &indexCtx) : IndexCtx(indexCtx) { } + bool Handled = true; + + bool VisitDecl(const Decl *D) { + Handled = false; + return true; + } + /// \brief Returns true if the given method has been defined explicitly by the /// user. static bool hasUserDefined(const ObjCMethodDecl *D, @@ -35,25 +43,35 @@ class IndexingDeclVisitor : public ConstDeclVisitor { const NamedDecl *Parent = nullptr) { if (!Parent) Parent = D; - if (!IndexCtx.shouldIndexFunctionLocalSymbols()) { - IndexCtx.indexTypeSourceInfo(D->getTypeSourceInfo(), Parent); - IndexCtx.indexNestedNameSpecifierLoc(D->getQualifierLoc(), Parent); - } else { + IndexCtx.indexTypeSourceInfo(D->getTypeSourceInfo(), Parent); + IndexCtx.indexNestedNameSpecifierLoc(D->getQualifierLoc(), Parent); + if (IndexCtx.shouldIndexFunctionLocalSymbols()) { + // Only index parameters in definitions, parameters in declarations are + // not useful. if (const ParmVarDecl *Parm = dyn_cast(D)) { - IndexCtx.handleVar(Parm); + auto *DC = Parm->getDeclContext(); + if (auto *FD = dyn_cast(DC)) { + if (FD->isThisDeclarationADefinition()) + IndexCtx.handleDecl(Parm); + } else if (auto *MD = dyn_cast(DC)) { + if (MD->isThisDeclarationADefinition()) + IndexCtx.handleDecl(Parm); + } else { + IndexCtx.handleDecl(Parm); + } } else if (const FunctionDecl *FD = dyn_cast(D)) { - for (auto PI : FD->params()) { - IndexCtx.handleVar(PI); + if (FD->isThisDeclarationADefinition()) { + for (auto PI : FD->params()) { + IndexCtx.handleDecl(PI); + } } } } } - void handleObjCMethod(const ObjCMethodDecl *D) { - IndexCtx.handleObjCMethod(D); - if (D->isImplicit()) - return; - + bool handleObjCMethod(const ObjCMethodDecl *D) { + if (!IndexCtx.handleDecl(D, (unsigned)SymbolRole::Dynamic)) + return false; IndexCtx.indexTypeSourceInfo(D->getReturnTypeSourceInfo(), D); for (const auto *I : D->params()) handleDeclarator(I, D); @@ -64,10 +82,26 @@ class IndexingDeclVisitor : public ConstDeclVisitor { IndexCtx.indexBody(Body, D, D); } } + return true; } bool VisitFunctionDecl(const FunctionDecl *D) { - IndexCtx.handleFunction(D); + if (D->isDeleted()) + return true; + + SymbolRoleSet Roles{}; + SmallVector Relations; + if (auto *CXXMD = dyn_cast(D)) { + if (CXXMD->isVirtual()) + Roles |= (unsigned)SymbolRole::Dynamic; + for (auto I = CXXMD->begin_overridden_methods(), + E = CXXMD->end_overridden_methods(); I != E; ++I) { + Relations.emplace_back((unsigned)SymbolRole::RelationOverrideOf, *I); + } + } + + if (!IndexCtx.handleDecl(D, Roles, Relations)) + return false; handleDeclarator(D); if (const CXXConstructorDecl *Ctor = dyn_cast(D)) { @@ -76,7 +110,8 @@ class IndexingDeclVisitor : public ConstDeclVisitor { if (Init->isWritten()) { IndexCtx.indexTypeSourceInfo(Init->getTypeSourceInfo(), D); if (const FieldDecl *Member = Init->getAnyMember()) - IndexCtx.handleReference(Member, Init->getMemberLocation(), D, D); + IndexCtx.handleReference(Member, Init->getMemberLocation(), D, D, + (unsigned)SymbolRole::Write); IndexCtx.indexBody(Init->getInit(), D, D); } } @@ -92,14 +127,16 @@ class IndexingDeclVisitor : public ConstDeclVisitor { } bool VisitVarDecl(const VarDecl *D) { - IndexCtx.handleVar(D); + if (!IndexCtx.handleDecl(D)) + return false; handleDeclarator(D); IndexCtx.indexBody(D->getInit(), D); return true; } bool VisitFieldDecl(const FieldDecl *D) { - IndexCtx.handleField(D); + if (!IndexCtx.handleDecl(D)) + return false; handleDeclarator(D); if (D->isBitField()) IndexCtx.indexBody(D->getBitWidth(), D); @@ -108,44 +145,77 @@ class IndexingDeclVisitor : public ConstDeclVisitor { return true; } + bool VisitObjCIvarDecl(const ObjCIvarDecl *D) { + if (D->getSynthesize()) { + // For synthesized ivars, use the location of the ObjC implementation, + // not the location of the property. + // Otherwise the header file containing the @interface will have different + // indexing contents based on whether the @implementation was present or + // not in the translation unit. + return IndexCtx.handleDecl(D, + cast(D->getDeclContext())->getLocation(), + (unsigned)SymbolRole::Implicit); + } + if (!IndexCtx.handleDecl(D)) + return false; + handleDeclarator(D); + return true; + } + bool VisitMSPropertyDecl(const MSPropertyDecl *D) { handleDeclarator(D); return true; } bool VisitEnumConstantDecl(const EnumConstantDecl *D) { - IndexCtx.handleEnumerator(D); + if (!IndexCtx.handleDecl(D)) + return false; IndexCtx.indexBody(D->getInitExpr(), D); return true; } bool VisitTypedefNameDecl(const TypedefNameDecl *D) { - IndexCtx.handleTypedefName(D); + if (!IndexCtx.handleDecl(D)) + return false; IndexCtx.indexTypeSourceInfo(D->getTypeSourceInfo(), D); return true; } bool VisitTagDecl(const TagDecl *D) { // Non-free standing tags are handled in indexTypeSourceInfo. - if (D->isFreeStanding()) - IndexCtx.indexTagDecl(D); + if (D->isFreeStanding()) { + if (D->isThisDeclarationADefinition()) { + IndexCtx.indexTagDecl(D); + } else { + auto *Parent = dyn_cast(D->getDeclContext()); + return IndexCtx.handleReference(D, D->getLocation(), Parent, + D->getLexicalDeclContext(), + SymbolRoleSet()); + } + } return true; } bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *D) { - IndexCtx.handleObjCInterface(D); - if (D->isThisDeclarationADefinition()) { + if (!IndexCtx.handleDecl(D)) + return false; IndexCtx.indexDeclContext(D); + } else { + return IndexCtx.handleReference(D, D->getLocation(), nullptr, nullptr, + SymbolRoleSet()); } return true; } bool VisitObjCProtocolDecl(const ObjCProtocolDecl *D) { - IndexCtx.handleObjCProtocol(D); - if (D->isThisDeclarationADefinition()) { + if (!IndexCtx.handleDecl(D)) + return false; IndexCtx.indexDeclContext(D); + } else { + return IndexCtx.handleReference(D, D->getLocation(), nullptr, nullptr, + SymbolRoleSet()); } return true; } @@ -156,9 +226,10 @@ class IndexingDeclVisitor : public ConstDeclVisitor { return true; if (Class->isImplicitInterfaceDecl()) - IndexCtx.handleObjCInterface(Class); + IndexCtx.handleDecl(Class); - IndexCtx.handleObjCImplementation(D); + if (!IndexCtx.handleDecl(D)) + return false; // Index the ivars first to make sure the synthesized ivars are indexed // before indexing the methods that can reference them. @@ -173,7 +244,8 @@ class IndexingDeclVisitor : public ConstDeclVisitor { } bool VisitObjCCategoryDecl(const ObjCCategoryDecl *D) { - IndexCtx.handleObjCCategory(D); + if (!IndexCtx.handleDecl(D)) + return false; IndexCtx.indexDeclContext(D); return true; } @@ -183,7 +255,8 @@ class IndexingDeclVisitor : public ConstDeclVisitor { if (!Cat) return true; - IndexCtx.handleObjCCategoryImpl(D); + if (!IndexCtx.handleDecl(D)) + return false; IndexCtx.indexDeclContext(D); return true; } @@ -205,14 +278,19 @@ class IndexingDeclVisitor : public ConstDeclVisitor { if (ObjCMethodDecl *MD = D->getSetterMethodDecl()) if (MD->getLexicalDeclContext() == D->getLexicalDeclContext()) handleObjCMethod(MD); - IndexCtx.handleObjCProperty(D); + if (!IndexCtx.handleDecl(D)) + return false; IndexCtx.indexTypeSourceInfo(D->getTypeSourceInfo(), D); return true; } bool VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *D) { ObjCPropertyDecl *PD = D->getPropertyDecl(); - IndexCtx.handleSynthesizedObjCProperty(D); + if (!IndexCtx.handleReference(PD, D->getLocation(), + /*Parent=*/cast(D->getDeclContext()), + D->getDeclContext(), SymbolRoleSet(), {}, + /*RefE=*/nullptr, D)) + return false; if (D->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) return true; @@ -221,121 +299,131 @@ class IndexingDeclVisitor : public ConstDeclVisitor { if (ObjCIvarDecl *IvarD = D->getPropertyIvarDecl()) { if (!IvarD->getSynthesize()) IndexCtx.handleReference(IvarD, D->getPropertyIvarDeclLoc(), nullptr, - D->getDeclContext()); + D->getDeclContext(), SymbolRoleSet()); } + auto *ImplD = cast(D->getDeclContext()); if (ObjCMethodDecl *MD = PD->getGetterMethodDecl()) { if (MD->isPropertyAccessor() && - !hasUserDefined(MD, cast(D->getDeclContext()))) - IndexCtx.handleSynthesizedObjCMethod(MD, D->getLocation(), - D->getLexicalDeclContext()); + !hasUserDefined(MD, ImplD)) + IndexCtx.handleDecl(MD, D->getLocation(), SymbolRoleSet(), {}, ImplD); } if (ObjCMethodDecl *MD = PD->getSetterMethodDecl()) { if (MD->isPropertyAccessor() && - !hasUserDefined(MD, cast(D->getDeclContext()))) - IndexCtx.handleSynthesizedObjCMethod(MD, D->getLocation(), - D->getLexicalDeclContext()); + !hasUserDefined(MD, ImplD)) + IndexCtx.handleDecl(MD, D->getLocation(), SymbolRoleSet(), {}, ImplD); } return true; } bool VisitNamespaceDecl(const NamespaceDecl *D) { - IndexCtx.handleNamespace(D); + if (!IndexCtx.handleDecl(D)) + return false; IndexCtx.indexDeclContext(D); return true; } bool VisitUsingDecl(const UsingDecl *D) { - // FIXME: Parent for the following is CXIdxEntity_Unexposed with no USR, - // we should do better. + const DeclContext *DC = D->getDeclContext()->getRedeclContext(); + const NamedDecl *Parent = dyn_cast(DC); - IndexCtx.indexNestedNameSpecifierLoc(D->getQualifierLoc(), D); + IndexCtx.indexNestedNameSpecifierLoc(D->getQualifierLoc(), Parent, + D->getLexicalDeclContext()); for (const auto *I : D->shadows()) - IndexCtx.handleReference(I->getUnderlyingDecl(), D->getLocation(), D, - D->getLexicalDeclContext()); + IndexCtx.handleReference(I->getUnderlyingDecl(), D->getLocation(), Parent, + D->getLexicalDeclContext(), SymbolRoleSet()); return true; } bool VisitUsingDirectiveDecl(const UsingDirectiveDecl *D) { - // FIXME: Parent for the following is CXIdxEntity_Unexposed with no USR, - // we should do better. - - IndexCtx.indexNestedNameSpecifierLoc(D->getQualifierLoc(), D); - IndexCtx.handleReference(D->getNominatedNamespaceAsWritten(), - D->getLocation(), D, D->getLexicalDeclContext()); - return true; - } - - bool VisitClassTemplateDecl(const ClassTemplateDecl *D) { - IndexCtx.handleClassTemplate(D); - if (D->isThisDeclarationADefinition()) - IndexCtx.indexDeclContext(D->getTemplatedDecl()); - return true; + const DeclContext *DC = D->getDeclContext()->getRedeclContext(); + const NamedDecl *Parent = dyn_cast(DC); + + IndexCtx.indexNestedNameSpecifierLoc(D->getQualifierLoc(), Parent, + D->getLexicalDeclContext()); + return IndexCtx.handleReference(D->getNominatedNamespaceAsWritten(), + D->getLocation(), Parent, + D->getLexicalDeclContext(), + SymbolRoleSet()); } bool VisitClassTemplateSpecializationDecl(const ClassTemplateSpecializationDecl *D) { // FIXME: Notify subsequent callbacks if info comes from implicit // instantiation. - if (D->isThisDeclarationADefinition() && - (IndexCtx.shouldIndexImplicitTemplateInsts() || - !IndexCtx.isTemplateImplicitInstantiation(D))) + if (D->isThisDeclarationADefinition()) IndexCtx.indexTagDecl(D); return true; } - bool VisitFunctionTemplateDecl(const FunctionTemplateDecl *D) { - IndexCtx.handleFunctionTemplate(D); - FunctionDecl *FD = D->getTemplatedDecl(); - handleDeclarator(FD, D); - if (FD->isThisDeclarationADefinition()) { - const Stmt *Body = FD->getBody(); - if (Body) { - IndexCtx.indexBody(Body, D, FD); - } - } - return true; + bool VisitTemplateDecl(const TemplateDecl *D) { + // FIXME: Template parameters. + return Visit(D->getTemplatedDecl()); } - bool VisitTypeAliasTemplateDecl(const TypeAliasTemplateDecl *D) { - IndexCtx.handleTypeAliasTemplate(D); - IndexCtx.indexTypeSourceInfo(D->getTemplatedDecl()->getTypeSourceInfo(), D); + bool VisitFriendDecl(const FriendDecl *D) { + if (auto ND = D->getFriendDecl()) { + // FIXME: Ignore a class template in a dependent context, these are not + // linked properly with their redeclarations, ending up with duplicate + // USRs. + // See comment "Friend templates are visible in fairly strange ways." in + // SemaTemplate.cpp which precedes code that prevents the friend template + // from becoming visible from the enclosing context. + if (isa(ND) && D->getDeclContext()->isDependentContext()) + return true; + return Visit(ND); + } + if (auto Ty = D->getFriendType()) { + IndexCtx.indexTypeSourceInfo(Ty, cast(D->getDeclContext())); + } return true; } bool VisitImportDecl(const ImportDecl *D) { - IndexCtx.importedModule(D); - return true; + return IndexCtx.importedModule(D); } }; } // anonymous namespace -void IndexingContext::indexDecl(const Decl *D) { +bool IndexingContext::indexDecl(const Decl *D) { if (D->isImplicit() && shouldIgnoreIfImplicit(D)) - return; + return true; - bool Handled = IndexingDeclVisitor(*this).Visit(D); - if (!Handled && isa(D)) - indexDeclContext(cast(D)); + if (isTemplateImplicitInstantiation(D)) + return true; + + IndexingDeclVisitor Visitor(*this); + bool ShouldContinue = Visitor.Visit(D); + if (!ShouldContinue) + return false; + + if (!Visitor.Handled && isa(D)) + return indexDeclContext(cast(D)); + + return true; } -void IndexingContext::indexDeclContext(const DeclContext *DC) { +bool IndexingContext::indexDeclContext(const DeclContext *DC) { for (const auto *I : DC->decls()) - indexDecl(I); + if (!indexDecl(I)) + return false; + return true; } -void IndexingContext::indexTopLevelDecl(const Decl *D) { - if (isNotFromSourceFile(D->getLocation())) - return; +bool IndexingContext::indexTopLevelDecl(const Decl *D) { + if (D->getLocation().isInvalid()) + return true; if (isa(D)) - return; // Wait for the objc container. + return true; // Wait for the objc container. - indexDecl(D); + return indexDecl(D); } -void IndexingContext::indexDeclGroupRef(DeclGroupRef DG) { +bool IndexingContext::indexDeclGroupRef(DeclGroupRef DG) { for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) - indexTopLevelDecl(*I); + if (!indexTopLevelDecl(*I)) + return false; + return true; } diff --git a/lib/Index/IndexSymbol.cpp b/lib/Index/IndexSymbol.cpp new file mode 100644 index 00000000000..c7c3c8c4d64 --- /dev/null +++ b/lib/Index/IndexSymbol.cpp @@ -0,0 +1,187 @@ +//===--- IndexSymbol.cpp - Types and functions for indexing symbols -------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/IndexSymbol.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" + +using namespace clang; +using namespace clang::index; + +SymbolInfo index::getSymbolInfo(const Decl *D) { + assert(D); + SymbolInfo Info; + Info.Kind = SymbolKind::Unknown; + Info.TemplateKind = SymbolCXXTemplateKind::NonTemplate; + Info.Lang = SymbolLanguage::C; + + if (const TagDecl *TD = dyn_cast(D)) { + switch (TD->getTagKind()) { + case TTK_Struct: + Info.Kind = SymbolKind::Struct; break; + case TTK_Union: + Info.Kind = SymbolKind::Union; break; + case TTK_Class: + Info.Kind = SymbolKind::CXXClass; + Info.Lang = SymbolLanguage::CXX; + break; + case TTK_Interface: + Info.Kind = SymbolKind::CXXInterface; + Info.Lang = SymbolLanguage::CXX; + break; + case TTK_Enum: + Info.Kind = SymbolKind::Enum; break; + } + + if (const CXXRecordDecl *CXXRec = dyn_cast(D)) + if (!CXXRec->isCLike()) + Info.Lang = SymbolLanguage::CXX; + + if (isa(D)) { + Info.TemplateKind = SymbolCXXTemplateKind::TemplatePartialSpecialization; + } else if (isa(D)) { + Info.TemplateKind = SymbolCXXTemplateKind::TemplateSpecialization; + } + + } else { + switch (D->getKind()) { + case Decl::Typedef: + Info.Kind = SymbolKind::Typedef; break; + case Decl::Function: + Info.Kind = SymbolKind::Function; + break; + case Decl::ParmVar: + Info.Kind = SymbolKind::Variable; + break; + case Decl::Var: + Info.Kind = SymbolKind::Variable; + if (isa(D->getDeclContext())) { + Info.Kind = SymbolKind::CXXStaticVariable; + Info.Lang = SymbolLanguage::CXX; + } + break; + case Decl::Field: + Info.Kind = SymbolKind::Field; + if (const CXXRecordDecl * + CXXRec = dyn_cast(D->getDeclContext())) { + if (!CXXRec->isCLike()) + Info.Lang = SymbolLanguage::CXX; + } + break; + case Decl::EnumConstant: + Info.Kind = SymbolKind::EnumConstant; break; + case Decl::ObjCInterface: + case Decl::ObjCImplementation: + Info.Kind = SymbolKind::ObjCClass; + Info.Lang = SymbolLanguage::ObjC; + break; + case Decl::ObjCProtocol: + Info.Kind = SymbolKind::ObjCProtocol; + Info.Lang = SymbolLanguage::ObjC; + break; + case Decl::ObjCCategory: + case Decl::ObjCCategoryImpl: + Info.Kind = SymbolKind::ObjCCategory; + Info.Lang = SymbolLanguage::ObjC; + break; + case Decl::ObjCMethod: + if (cast(D)->isInstanceMethod()) + Info.Kind = SymbolKind::ObjCInstanceMethod; + else + Info.Kind = SymbolKind::ObjCClassMethod; + Info.Lang = SymbolLanguage::ObjC; + break; + case Decl::ObjCProperty: + Info.Kind = SymbolKind::ObjCProperty; + Info.Lang = SymbolLanguage::ObjC; + break; + case Decl::ObjCIvar: + Info.Kind = SymbolKind::ObjCIvar; + Info.Lang = SymbolLanguage::ObjC; + break; + case Decl::Namespace: + Info.Kind = SymbolKind::CXXNamespace; + Info.Lang = SymbolLanguage::CXX; + break; + case Decl::NamespaceAlias: + Info.Kind = SymbolKind::CXXNamespaceAlias; + Info.Lang = SymbolLanguage::CXX; + break; + case Decl::CXXConstructor: + Info.Kind = SymbolKind::CXXConstructor; + Info.Lang = SymbolLanguage::CXX; + break; + case Decl::CXXDestructor: + Info.Kind = SymbolKind::CXXDestructor; + Info.Lang = SymbolLanguage::CXX; + break; + case Decl::CXXConversion: + Info.Kind = SymbolKind::CXXConversionFunction; + Info.Lang = SymbolLanguage::CXX; + break; + case Decl::CXXMethod: { + const CXXMethodDecl *MD = cast(D); + if (MD->isStatic()) + Info.Kind = SymbolKind::CXXStaticMethod; + else + Info.Kind = SymbolKind::CXXInstanceMethod; + Info.Lang = SymbolLanguage::CXX; + break; + } + case Decl::ClassTemplate: + Info.Kind = SymbolKind::CXXClass; + Info.TemplateKind = SymbolCXXTemplateKind::Template; + break; + case Decl::FunctionTemplate: + Info.Kind = SymbolKind::Function; + Info.TemplateKind = SymbolCXXTemplateKind::Template; + if (const CXXMethodDecl *MD = dyn_cast_or_null( + cast(D)->getTemplatedDecl())) { + if (isa(MD)) + Info.Kind = SymbolKind::CXXConstructor; + else if (isa(MD)) + Info.Kind = SymbolKind::CXXDestructor; + else if (isa(MD)) + Info.Kind = SymbolKind::CXXConversionFunction; + else { + if (MD->isStatic()) + Info.Kind = SymbolKind::CXXStaticMethod; + else + Info.Kind = SymbolKind::CXXInstanceMethod; + } + } + break; + case Decl::TypeAliasTemplate: + Info.Kind = SymbolKind::CXXTypeAlias; + Info.TemplateKind = SymbolCXXTemplateKind::Template; + break; + case Decl::TypeAlias: + Info.Kind = SymbolKind::CXXTypeAlias; + Info.Lang = SymbolLanguage::CXX; + break; + default: + break; + } + } + + if (Info.Kind == SymbolKind::Unknown) + return Info; + + if (const FunctionDecl *FD = dyn_cast(D)) { + if (FD->getTemplatedKind() == + FunctionDecl::TK_FunctionTemplateSpecialization) + Info.TemplateKind = SymbolCXXTemplateKind::TemplateSpecialization; + } + + if (Info.TemplateKind != SymbolCXXTemplateKind::NonTemplate) + Info.Lang = SymbolLanguage::CXX; + + return Info; +} diff --git a/tools/libclang/IndexTypeSourceInfo.cpp b/lib/Index/IndexTypeSourceInfo.cpp similarity index 52% rename from tools/libclang/IndexTypeSourceInfo.cpp rename to lib/Index/IndexTypeSourceInfo.cpp index 9666052ed18..619a9a48bef 100644 --- a/tools/libclang/IndexTypeSourceInfo.cpp +++ b/lib/Index/IndexTypeSourceInfo.cpp @@ -1,4 +1,4 @@ -//===- CIndexHigh.cpp - Higher level API functions ------------------------===// +//===- IndexTypeSourceInfo.cpp - Indexing types ---------------------------===// // // The LLVM Compiler Infrastructure // @@ -11,7 +11,7 @@ #include "clang/AST/RecursiveASTVisitor.h" using namespace clang; -using namespace cxindex; +using namespace index; namespace { @@ -19,20 +19,58 @@ class TypeIndexer : public RecursiveASTVisitor { IndexingContext &IndexCtx; const NamedDecl *Parent; const DeclContext *ParentDC; + bool IsBase; + SmallVector Relations; + + typedef RecursiveASTVisitor base; public: TypeIndexer(IndexingContext &indexCtx, const NamedDecl *parent, - const DeclContext *DC) - : IndexCtx(indexCtx), Parent(parent), ParentDC(DC) { } + const DeclContext *DC, bool isBase) + : IndexCtx(indexCtx), Parent(parent), ParentDC(DC), IsBase(isBase) { + if (IsBase) { + assert(Parent); + Relations.emplace_back((unsigned)SymbolRole::RelationBaseOf, Parent); + } + } bool shouldWalkTypesOfTypeLocs() const { return false; } bool VisitTypedefTypeLoc(TypedefTypeLoc TL) { - IndexCtx.handleReference(TL.getTypedefNameDecl(), TL.getNameLoc(), - Parent, ParentDC); + return IndexCtx.handleReference(TL.getTypedefNameDecl(), TL.getNameLoc(), + Parent, ParentDC, SymbolRoleSet(), + Relations); + } + +#define TRY_TO(CALL_EXPR) \ + do { \ + if (!CALL_EXPR) \ + return false; \ + } while (0) + + bool traverseParamVarHelper(ParmVarDecl *D) { + TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc())); + if (D->getTypeSourceInfo()) + TRY_TO(TraverseTypeLoc(D->getTypeSourceInfo()->getTypeLoc())); return true; } + bool TraverseParmVarDecl(ParmVarDecl *D) { + // Avoid visiting default arguments from the definition that were already + // visited in the declaration. + // FIXME: A free function definition can have default arguments. + // Avoiding double visitaiton of default arguments should be handled by the + // visitor probably with a bit in the AST to indicate if the attached + // default argument was 'inherited' or written in source. + if (auto FD = dyn_cast(D->getDeclContext())) { + if (FD->isThisDeclarationADefinition()) { + return traverseParamVarHelper(D); + } + } + + return base::TraverseParmVarDecl(D); + } + bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) { IndexCtx.indexNestedNameSpecifierLoc(NNS, Parent, ParentDC); return true; @@ -48,24 +86,20 @@ class TypeIndexer : public RecursiveASTVisitor { return true; } - if (D->getLocation() == TL.getNameLoc()) - IndexCtx.handleTagDecl(D); - else - IndexCtx.handleReference(D, TL.getNameLoc(), - Parent, ParentDC); - return true; + return IndexCtx.handleReference(D, TL.getNameLoc(), + Parent, ParentDC, SymbolRoleSet(), + Relations); } bool VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc TL) { - IndexCtx.handleReference(TL.getIFaceDecl(), TL.getNameLoc(), - Parent, ParentDC); - return true; + return IndexCtx.handleReference(TL.getIFaceDecl(), TL.getNameLoc(), + Parent, ParentDC, SymbolRoleSet()); } bool VisitObjCObjectTypeLoc(ObjCObjectTypeLoc TL) { for (unsigned i = 0, e = TL.getNumProtocols(); i != e; ++i) { IndexCtx.handleReference(TL.getProtocol(i), TL.getProtocolLoc(i), - Parent, ParentDC); + Parent, ParentDC, SymbolRoleSet()); } return true; } @@ -75,11 +109,11 @@ class TypeIndexer : public RecursiveASTVisitor { if (IndexCtx.shouldIndexImplicitTemplateInsts()) { if (CXXRecordDecl *RD = T->getAsCXXRecordDecl()) IndexCtx.handleReference(RD, TL.getTemplateNameLoc(), - Parent, ParentDC); + Parent, ParentDC, SymbolRoleSet(), Relations); } else { if (const TemplateDecl *D = T->getTemplateName().getAsTemplateDecl()) IndexCtx.handleReference(D, TL.getTemplateNameLoc(), - Parent, ParentDC); + Parent, ParentDC, SymbolRoleSet(), Relations); } } return true; @@ -95,22 +129,24 @@ class TypeIndexer : public RecursiveASTVisitor { void IndexingContext::indexTypeSourceInfo(TypeSourceInfo *TInfo, const NamedDecl *Parent, - const DeclContext *DC) { + const DeclContext *DC, + bool isBase) { if (!TInfo || TInfo->getTypeLoc().isNull()) return; - indexTypeLoc(TInfo->getTypeLoc(), Parent, DC); + indexTypeLoc(TInfo->getTypeLoc(), Parent, DC, isBase); } void IndexingContext::indexTypeLoc(TypeLoc TL, const NamedDecl *Parent, - const DeclContext *DC) { + const DeclContext *DC, + bool isBase) { if (TL.isNull()) return; if (!DC) DC = Parent->getLexicalDeclContext(); - TypeIndexer(*this, Parent, DC).TraverseTypeLoc(TL); + TypeIndexer(*this, Parent, DC, isBase).TraverseTypeLoc(TL); } void IndexingContext::indexNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS, @@ -134,11 +170,11 @@ void IndexingContext::indexNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS, case NestedNameSpecifier::Namespace: handleReference(NNS.getNestedNameSpecifier()->getAsNamespace(), - Loc, Parent, DC); + Loc, Parent, DC, SymbolRoleSet()); break; case NestedNameSpecifier::NamespaceAlias: handleReference(NNS.getNestedNameSpecifier()->getAsNamespaceAlias(), - Loc, Parent, DC); + Loc, Parent, DC, SymbolRoleSet()); break; case NestedNameSpecifier::TypeSpec: @@ -149,8 +185,18 @@ void IndexingContext::indexNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS, } void IndexingContext::indexTagDecl(const TagDecl *D) { - if (handleTagDecl(D)) { - if (D->isThisDeclarationADefinition()) + if (!shouldIndexFunctionLocalSymbols() && isFunctionLocalDecl(D)) + return; + + if (handleDecl(D)) { + if (D->isThisDeclarationADefinition()) { + indexNestedNameSpecifierLoc(D->getQualifierLoc(), D); + if (auto CXXRD = dyn_cast(D)) { + for (const auto &I : CXXRD->bases()) { + indexTypeSourceInfo(I.getTypeSourceInfo(), CXXRD, CXXRD, /*isBase=*/true); + } + } indexDeclContext(D); + } } } diff --git a/lib/Index/IndexingAction.cpp b/lib/Index/IndexingAction.cpp new file mode 100644 index 00000000000..3f7ef43e7dc --- /dev/null +++ b/lib/Index/IndexingAction.cpp @@ -0,0 +1,140 @@ +//===- IndexingAction.cpp - Frontend index action -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/IndexingAction.h" +#include "clang/Index/IndexDataConsumer.h" +#include "IndexingContext.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Frontend/MultiplexConsumer.h" +#include "clang/Lex/Preprocessor.h" + +using namespace clang; +using namespace clang::index; + +void IndexDataConsumer::_anchor() {} + +bool IndexDataConsumer::handleDeclOccurence(const Decl *D, SymbolRoleSet Roles, + ArrayRef Relations, + FileID FID, unsigned Offset, + ASTNodeInfo ASTNode) { + return true; +} + +bool IndexDataConsumer::handleMacroOccurence(const IdentifierInfo *Name, + const MacroInfo *MI, SymbolRoleSet Roles, + FileID FID, unsigned Offset) { + return true; +} + +bool IndexDataConsumer::handleModuleOccurence(const ImportDecl *ImportD, + SymbolRoleSet Roles, + FileID FID, unsigned Offset) { + return true; +} + +namespace { + +class IndexASTConsumer : public ASTConsumer { + IndexingContext &IndexCtx; + +public: + IndexASTConsumer(IndexingContext &IndexCtx) + : IndexCtx(IndexCtx) {} + +protected: + void Initialize(ASTContext &Context) override { + IndexCtx.setASTContext(Context); + } + + bool HandleTopLevelDecl(DeclGroupRef DG) override { + return IndexCtx.indexDeclGroupRef(DG); + } + + void HandleInterestingDecl(DeclGroupRef DG) override { + // Ignore deserialized decls. + } + + void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) override { + IndexCtx.indexDeclGroupRef(DG); + } + + void HandleTranslationUnit(ASTContext &Ctx) override { + } +}; + +class IndexAction : public WrapperFrontendAction { + IndexingOptions IndexOpts; + std::shared_ptr DataConsumer; + std::unique_ptr IndexCtx; + +public: + IndexAction(std::unique_ptr WrappedAction, + std::shared_ptr DataConsumer, + IndexingOptions Opts) + : WrapperFrontendAction(std::move(WrappedAction)), + IndexOpts(Opts), + DataConsumer(std::move(DataConsumer)) {} + +protected: + std::unique_ptr CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override; + void EndSourceFileAction() override; +}; + +} // anonymous namespace + +void IndexAction::EndSourceFileAction() { + // Invoke wrapped action's method. + WrapperFrontendAction::EndSourceFileAction(); + + bool IndexActionFailed = !IndexCtx; + if (!IndexActionFailed) + DataConsumer->finish(); +} + +std::unique_ptr +IndexAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + auto OtherConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile); + if (!OtherConsumer) + return nullptr; + + IndexCtx.reset(new IndexingContext(IndexOpts, *DataConsumer)); + + std::vector> Consumers; + Consumers.push_back(std::move(OtherConsumer)); + Consumers.push_back(llvm::make_unique(*IndexCtx)); + return llvm::make_unique(std::move(Consumers)); +} + +std::unique_ptr +index::createIndexingAction(std::unique_ptr WrappedAction, + std::shared_ptr DataConsumer, + IndexingOptions Opts) { + return llvm::make_unique(std::move(WrappedAction), + std::move(DataConsumer), + Opts); +} + + +static bool topLevelDeclVisitor(void *context, const Decl *D) { + IndexingContext &IndexCtx = *static_cast(context); + return IndexCtx.indexTopLevelDecl(D); +} + +static void indexTranslationUnit(ASTUnit &Unit, IndexingContext &IndexCtx) { + Unit.visitLocalTopLevelDecls(&IndexCtx, topLevelDeclVisitor); +} + +void index::indexASTUnit(ASTUnit &Unit, + std::shared_ptr DataConsumer, + IndexingOptions Opts) { + IndexingContext IndexCtx(Opts, *DataConsumer); + IndexCtx.setASTContext(Unit.getASTContext()); + indexTranslationUnit(Unit, IndexCtx); +} diff --git a/lib/Index/IndexingContext.cpp b/lib/Index/IndexingContext.cpp new file mode 100644 index 00000000000..91fbbfb2684 --- /dev/null +++ b/lib/Index/IndexingContext.cpp @@ -0,0 +1,325 @@ +//===- IndexingContext.cpp - Indexing context data ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "IndexingContext.h" +#include "clang/Index/IndexDataConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/DeclObjC.h" +#include "clang/Basic/SourceManager.h" + +using namespace clang; +using namespace index; + +bool IndexingContext::shouldIndexFunctionLocalSymbols() const { + return IndexOpts.IndexFunctionLocals; +} + +bool IndexingContext::handleDecl(const Decl *D, + SymbolRoleSet Roles, + ArrayRef Relations) { + return handleDeclOccurrence(D, D->getLocation(), /*IsRef=*/false, + cast(D->getDeclContext()), Roles, Relations, + nullptr, nullptr, D->getDeclContext()); +} + +bool IndexingContext::handleDecl(const Decl *D, SourceLocation Loc, + SymbolRoleSet Roles, + ArrayRef Relations, + const DeclContext *DC) { + if (!DC) + DC = D->getDeclContext(); + return handleDeclOccurrence(D, Loc, /*IsRef=*/false, cast(DC), + Roles, Relations, + nullptr, nullptr, DC); +} + +bool IndexingContext::handleReference(const NamedDecl *D, SourceLocation Loc, + const NamedDecl *Parent, + const DeclContext *DC, + SymbolRoleSet Roles, + ArrayRef Relations, + const Expr *RefE, + const Decl *RefD) { + if (!shouldIndexFunctionLocalSymbols() && isFunctionLocalDecl(D)) + return true; + + if (isa(D) || isa(D)) + return true; + + return handleDeclOccurrence(D, Loc, /*IsRef=*/true, Parent, Roles, Relations, + RefE, RefD, DC); +} + +bool IndexingContext::importedModule(const ImportDecl *ImportD) { + SourceLocation Loc = ImportD->getLocation(); + SourceManager &SM = Ctx->getSourceManager(); + Loc = SM.getFileLoc(Loc); + if (Loc.isInvalid()) + return true; + + FileID FID; + unsigned Offset; + std::tie(FID, Offset) = SM.getDecomposedLoc(Loc); + if (FID.isInvalid()) + return true; + + bool Invalid = false; + const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, &Invalid); + if (Invalid || !SEntry.isFile()) + return true; + + if (SEntry.getFile().getFileCharacteristic() != SrcMgr::C_User) { + switch (IndexOpts.SystemSymbolFilter) { + case IndexingOptions::SystemSymbolFilterKind::None: + case IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly: + return true; + case IndexingOptions::SystemSymbolFilterKind::All: + break; + } + } + + SymbolRoleSet Roles{}; + if (ImportD->isImplicit()) + Roles |= (unsigned)SymbolRole::Implicit; + + return DataConsumer.handleModuleOccurence(ImportD, Roles, FID, Offset); +} + +bool IndexingContext::isFunctionLocalDecl(const Decl *D) { + assert(D); + + if (isa(D)) + return true; + + if (!D->getParentFunctionOrMethod()) + return false; + + if (const NamedDecl *ND = dyn_cast(D)) { + switch (ND->getFormalLinkage()) { + case NoLinkage: + case VisibleNoLinkage: + case InternalLinkage: + return true; + case UniqueExternalLinkage: + llvm_unreachable("Not a sema linkage"); + case ExternalLinkage: + return false; + } + } + + return true; +} + +bool IndexingContext::isTemplateImplicitInstantiation(const Decl *D) { + TemplateSpecializationKind TKind = TSK_Undeclared; + if (const ClassTemplateSpecializationDecl * + SD = dyn_cast(D)) { + TKind = SD->getSpecializationKind(); + } + if (const FunctionDecl *FD = dyn_cast(D)) { + TKind = FD->getTemplateSpecializationKind(); + } + switch (TKind) { + case TSK_Undeclared: + case TSK_ExplicitSpecialization: + return false; + case TSK_ImplicitInstantiation: + case TSK_ExplicitInstantiationDeclaration: + case TSK_ExplicitInstantiationDefinition: + return true; + } +} + +bool IndexingContext::shouldIgnoreIfImplicit(const Decl *D) { + if (isa(D)) + return false; + if (isa(D)) + return false; + if (isa(D)) + return false; + if (isa(D)) + return false; + if (isa(D)) + return false; + return true; +} + +static const Decl *adjustTemplateImplicitInstantiation(const Decl *D) { + if (const ClassTemplateSpecializationDecl * + SD = dyn_cast(D)) { + return SD->getTemplateInstantiationPattern(); + } + if (const FunctionDecl *FD = dyn_cast(D)) { + return FD->getTemplateInstantiationPattern(); + } + return nullptr; +} + +static bool isDeclADefinition(const Decl *D, ASTContext &Ctx) { + if (auto VD = dyn_cast(D)) + return VD->isThisDeclarationADefinition(Ctx); + + if (auto FD = dyn_cast(D)) + return FD->isThisDeclarationADefinition(); + + if (auto TD = dyn_cast(D)) + return TD->isThisDeclarationADefinition(); + + if (auto MD = dyn_cast(D)) + return MD->isThisDeclarationADefinition(); + + if (isa(D) || + isa(D) || + isa(D) || + isa(D) || + isa(D) || + isa(D)) + return true; + + return false; +} + +static const Decl *adjustParent(const Decl *Parent) { + if (!Parent) + return nullptr; + for (;; Parent = cast(Parent->getDeclContext())) { + if (isa(Parent)) + return nullptr; + if (isa(Parent) || isa(Parent)) + continue; + if (auto NS = dyn_cast(Parent)) { + if (NS->isAnonymousNamespace()) + continue; + } else if (auto EnumD = dyn_cast(Parent)) { + // Move enumerators under anonymous enum to the enclosing parent. + if (EnumD->getDeclName().isEmpty()) + continue; + } else if (auto RD = dyn_cast(Parent)) { + if (RD->isAnonymousStructOrUnion()) + continue; + } else if (auto FD = dyn_cast(Parent)) { + if (FD->getDeclName().isEmpty()) + continue; + } + return Parent; + } +} + +static const Decl *getCanonicalDecl(const Decl *D) { + D = D->getCanonicalDecl(); + if (auto TD = dyn_cast(D)) { + D = TD->getTemplatedDecl(); + assert(D->isCanonicalDecl()); + } + + return D; +} + +bool IndexingContext::handleDeclOccurrence(const Decl *D, SourceLocation Loc, + bool IsRef, const Decl *Parent, + SymbolRoleSet Roles, + ArrayRef Relations, + const Expr *OrigE, + const Decl *OrigD, + const DeclContext *ContainerDC) { + if (D->isImplicit() && !isa(D)) + return true; + if (!isa(D) || + (cast(D)->getDeclName().isEmpty() && + !isa(D) && !isa(D))) + return true; + + SourceManager &SM = Ctx->getSourceManager(); + Loc = SM.getFileLoc(Loc); + if (Loc.isInvalid()) + return true; + + FileID FID; + unsigned Offset; + std::tie(FID, Offset) = SM.getDecomposedLoc(Loc); + if (FID.isInvalid()) + return true; + + bool Invalid = false; + const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, &Invalid); + if (Invalid || !SEntry.isFile()) + return true; + + if (SEntry.getFile().getFileCharacteristic() != SrcMgr::C_User) { + switch (IndexOpts.SystemSymbolFilter) { + case IndexingOptions::SystemSymbolFilterKind::None: + return true; + case IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly: + if (IsRef) + return true; + break; + case IndexingOptions::SystemSymbolFilterKind::All: + break; + } + } + + if (isTemplateImplicitInstantiation(D)) { + if (!IsRef) + return true; + D = adjustTemplateImplicitInstantiation(D); + if (!D) + return true; + assert(!isTemplateImplicitInstantiation(D)); + } + + if (!OrigD) + OrigD = D; + + if (IsRef) + Roles |= (unsigned)SymbolRole::Reference; + else if (isDeclADefinition(D, *Ctx)) + Roles |= (unsigned)SymbolRole::Definition; + else + Roles |= (unsigned)SymbolRole::Declaration; + + D = getCanonicalDecl(D); + if (D->isImplicit() && !isa(D)) { + // operator new declarations will link to the implicit one as canonical. + return true; + } + Parent = adjustParent(Parent); + if (Parent) + Parent = getCanonicalDecl(Parent); + assert(!Parent || !Parent->isImplicit() || + isa(Parent) || isa(Parent)); + + SmallVector FinalRelations; + FinalRelations.reserve(Relations.size()+1); + + auto addRelation = [&](SymbolRelation Rel) { + auto It = std::find_if(FinalRelations.begin(), FinalRelations.end(), + [&](SymbolRelation Elem)->bool { + return Elem.RelatedSymbol == Rel.RelatedSymbol; + }); + if (It != FinalRelations.end()) { + It->Roles |= Rel.Roles; + } else { + FinalRelations.push_back(Rel); + } + Roles |= Rel.Roles; + }; + + if (!IsRef && Parent && !cast(Parent)->isFunctionOrMethod()) { + addRelation(SymbolRelation{(unsigned)SymbolRole::RelationChildOf, Parent}); + } + for (auto &Rel : Relations) { + addRelation(SymbolRelation(Rel.Roles, + Rel.RelatedSymbol->getCanonicalDecl())); + } + + IndexDataConsumer::ASTNodeInfo Node{ OrigE, OrigD, Parent, ContainerDC }; + return DataConsumer.handleDeclOccurence(D, Roles, FinalRelations, FID, Offset, + Node); +} diff --git a/lib/Index/IndexingContext.h b/lib/Index/IndexingContext.h new file mode 100644 index 00000000000..774650547f8 --- /dev/null +++ b/lib/Index/IndexingContext.h @@ -0,0 +1,121 @@ +//===- IndexingContext.h - Indexing context data ----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_INDEX_INDEXINGCONTEXT_H +#define LLVM_CLANG_LIB_INDEX_INDEXINGCONTEXT_H + +#include "clang/Basic/LLVM.h" +#include "clang/Index/IndexSymbol.h" +#include "clang/Index/IndexingAction.h" +#include "llvm/ADT/ArrayRef.h" + +namespace clang { + class ASTContext; + class Decl; + class DeclGroupRef; + class ImportDecl; + class TagDecl; + class TypeSourceInfo; + class NamedDecl; + class ObjCMethodDecl; + class DeclContext; + class NestedNameSpecifierLoc; + class Stmt; + class Expr; + class TypeLoc; + class SourceLocation; + +namespace index { + class IndexDataConsumer; + +class IndexingContext { + IndexingOptions IndexOpts; + IndexDataConsumer &DataConsumer; + ASTContext *Ctx = nullptr; + +public: + IndexingContext(IndexingOptions IndexOpts, IndexDataConsumer &DataConsumer) + : IndexOpts(IndexOpts), DataConsumer(DataConsumer) {} + + const IndexingOptions &getIndexOpts() const { return IndexOpts; } + IndexDataConsumer &getDataConsumer() { return DataConsumer; } + + void setASTContext(ASTContext &ctx) { Ctx = &ctx; } + + bool shouldSuppressRefs() const { + return false; + } + + bool shouldIndexFunctionLocalSymbols() const; + + bool shouldIndexImplicitTemplateInsts() const { + return false; + } + + static bool isFunctionLocalDecl(const Decl *D); + static bool isTemplateImplicitInstantiation(const Decl *D); + + bool handleDecl(const Decl *D, SymbolRoleSet Roles = SymbolRoleSet(), + ArrayRef Relations = {}); + + bool handleDecl(const Decl *D, SourceLocation Loc, + SymbolRoleSet Roles = SymbolRoleSet(), + ArrayRef Relations = {}, + const DeclContext *DC = nullptr); + + bool handleReference(const NamedDecl *D, SourceLocation Loc, + const NamedDecl *Parent, + const DeclContext *DC, + SymbolRoleSet Roles, + ArrayRef Relations = {}, + const Expr *RefE = nullptr, + const Decl *RefD = nullptr); + + bool importedModule(const ImportDecl *ImportD); + + bool indexDecl(const Decl *D); + + void indexTagDecl(const TagDecl *D); + + void indexTypeSourceInfo(TypeSourceInfo *TInfo, const NamedDecl *Parent, + const DeclContext *DC = nullptr, + bool isBase = false); + + void indexTypeLoc(TypeLoc TL, const NamedDecl *Parent, + const DeclContext *DC = nullptr, + bool isBase = false); + + void indexNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS, + const NamedDecl *Parent, + const DeclContext *DC = nullptr); + + bool indexDeclContext(const DeclContext *DC); + + void indexBody(const Stmt *S, const NamedDecl *Parent, + const DeclContext *DC = nullptr); + + bool indexTopLevelDecl(const Decl *D); + bool indexDeclGroupRef(DeclGroupRef DG); + +private: + bool shouldIgnoreIfImplicit(const Decl *D); + + bool handleDeclOccurrence(const Decl *D, SourceLocation Loc, + bool IsRef, const Decl *Parent, + SymbolRoleSet Roles, + ArrayRef Relations, + const Expr *RefE, + const Decl *RefD, + const DeclContext *ContainerDC); +}; + +} // end namespace index +} // end namespace clang + +#endif diff --git a/tools/libclang/CMakeLists.txt b/tools/libclang/CMakeLists.txt index 56d053447f6..0dfad30ac28 100644 --- a/tools/libclang/CMakeLists.txt +++ b/tools/libclang/CMakeLists.txt @@ -11,17 +11,14 @@ set(SOURCES CIndexer.cpp CXComment.cpp CXCursor.cpp + CXIndexDataConsumer.cpp CXCompilationDatabase.cpp CXLoadedDiagnostic.cpp CXSourceLocation.cpp CXStoredDiagnostic.cpp CXString.cpp CXType.cpp - IndexBody.cpp - IndexDecl.cpp - IndexTypeSourceInfo.cpp Indexing.cpp - IndexingContext.cpp ADDITIONAL_HEADERS CIndexDiagnostic.h diff --git a/tools/libclang/IndexingContext.cpp b/tools/libclang/CXIndexDataConsumer.cpp similarity index 70% rename from tools/libclang/IndexingContext.cpp rename to tools/libclang/CXIndexDataConsumer.cpp index 7f65412f989..f6f8e306794 100644 --- a/tools/libclang/IndexingContext.cpp +++ b/tools/libclang/CXIndexDataConsumer.cpp @@ -1,4 +1,4 @@ -//===- IndexingContext.cpp - Higher level API functions -------------------===// +//===- CXIndexDataConsumer.cpp - Index data consumer for libclang----------===// // // The LLVM Compiler Infrastructure // @@ -7,21 +7,219 @@ // //===----------------------------------------------------------------------===// -#include "IndexingContext.h" +#include "CXIndexDataConsumer.h" #include "CIndexDiagnostic.h" #include "CXTranslationUnit.h" #include "clang/AST/Attr.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" +#include "clang/AST/DeclVisitor.h" #include "clang/Frontend/ASTUnit.h" using namespace clang; +using namespace clang::index; using namespace cxindex; using namespace cxcursor; -IndexingContext::ObjCProtocolListInfo::ObjCProtocolListInfo( +namespace { +class IndexingDeclVisitor : public ConstDeclVisitor { + CXIndexDataConsumer &DataConsumer; + SourceLocation DeclLoc; + const DeclContext *LexicalDC; + +public: + IndexingDeclVisitor(CXIndexDataConsumer &dataConsumer, SourceLocation Loc, + const DeclContext *lexicalDC) + : DataConsumer(dataConsumer), DeclLoc(Loc), LexicalDC(lexicalDC) { } + + bool VisitFunctionDecl(const FunctionDecl *D) { + DataConsumer.handleFunction(D); + return true; + } + + bool VisitVarDecl(const VarDecl *D) { + DataConsumer.handleVar(D); + return true; + } + + bool VisitFieldDecl(const FieldDecl *D) { + DataConsumer.handleField(D); + return true; + } + + bool VisitMSPropertyDecl(const MSPropertyDecl *D) { + return true; + } + + bool VisitEnumConstantDecl(const EnumConstantDecl *D) { + DataConsumer.handleEnumerator(D); + return true; + } + + bool VisitTypedefNameDecl(const TypedefNameDecl *D) { + DataConsumer.handleTypedefName(D); + return true; + } + + bool VisitTagDecl(const TagDecl *D) { + DataConsumer.handleTagDecl(D); + return true; + } + + bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *D) { + DataConsumer.handleObjCInterface(D); + return true; + } + + bool VisitObjCProtocolDecl(const ObjCProtocolDecl *D) { + DataConsumer.handleObjCProtocol(D); + return true; + } + + bool VisitObjCImplementationDecl(const ObjCImplementationDecl *D) { + DataConsumer.handleObjCImplementation(D); + return true; + } + + bool VisitObjCCategoryDecl(const ObjCCategoryDecl *D) { + DataConsumer.handleObjCCategory(D); + return true; + } + + bool VisitObjCCategoryImplDecl(const ObjCCategoryImplDecl *D) { + DataConsumer.handleObjCCategoryImpl(D); + return true; + } + + bool VisitObjCMethodDecl(const ObjCMethodDecl *D) { + if (D->getDeclContext() != LexicalDC) + DataConsumer.handleSynthesizedObjCMethod(D, DeclLoc, LexicalDC); + else + DataConsumer.handleObjCMethod(D); + return true; + } + + bool VisitObjCPropertyDecl(const ObjCPropertyDecl *D) { + DataConsumer.handleObjCProperty(D); + return true; + } + + bool VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *D) { + DataConsumer.handleSynthesizedObjCProperty(D); + return true; + } + + bool VisitNamespaceDecl(const NamespaceDecl *D) { + DataConsumer.handleNamespace(D); + return true; + } + + bool VisitUsingDecl(const UsingDecl *D) { + return true; + } + + bool VisitUsingDirectiveDecl(const UsingDirectiveDecl *D) { + return true; + } + + bool VisitClassTemplateDecl(const ClassTemplateDecl *D) { + DataConsumer.handleClassTemplate(D); + return true; + } + + bool VisitClassTemplateSpecializationDecl(const + ClassTemplateSpecializationDecl *D) { + DataConsumer.handleTagDecl(D); + return true; + } + + bool VisitFunctionTemplateDecl(const FunctionTemplateDecl *D) { + DataConsumer.handleFunctionTemplate(D); + return true; + } + + bool VisitTypeAliasTemplateDecl(const TypeAliasTemplateDecl *D) { + DataConsumer.handleTypeAliasTemplate(D); + return true; + } + + bool VisitImportDecl(const ImportDecl *D) { + DataConsumer.importedModule(D); + return true; + } +}; +} + +bool CXIndexDataConsumer::handleDeclOccurence(const Decl *D, + SymbolRoleSet Roles, + ArrayRef Relations, + FileID FID, unsigned Offset, + ASTNodeInfo ASTNode) { + SourceLocation Loc = getASTContext().getSourceManager() + .getLocForStartOfFile(FID).getLocWithOffset(Offset); + + if (Roles & (unsigned)SymbolRole::Reference) { + const NamedDecl *ND = dyn_cast(D); + if (!ND) + return true; + + if (auto *ObjCID = dyn_cast_or_null(ASTNode.OrigD)) { + if (!ObjCID->isThisDeclarationADefinition() && + ObjCID->getLocation() == Loc) { + // The libclang API treats this as ObjCClassRef declaration. + IndexingDeclVisitor(*this, Loc, nullptr).Visit(ObjCID); + return true; + } + } + + CXIdxEntityRefKind Kind = CXIdxEntityRef_Direct; + if (Roles & (unsigned)SymbolRole::Implicit) { + Kind = CXIdxEntityRef_Implicit; + } + + CXCursor Cursor; + if (ASTNode.OrigE) { + Cursor = cxcursor::MakeCXCursor(ASTNode.OrigE, + cast(ASTNode.ContainerDC), + getCXTU()); + } else { + const NamedDecl *CursorD = dyn_cast_or_null(ASTNode.OrigD); + if (!CursorD) + CursorD = ND; + Cursor = getRefCursor(CursorD, Loc); + } + handleReference(ND, Loc, Cursor, + dyn_cast_or_null(ASTNode.Parent), + ASTNode.ContainerDC, ASTNode.OrigE, Kind); + + } else { + const DeclContext *DC = nullptr; + for (const auto &SymRel : Relations) { + if (SymRel.Roles & (unsigned)SymbolRole::RelationChildOf) + DC = dyn_cast(SymRel.RelatedSymbol); + } + IndexingDeclVisitor(*this, Loc, DC).Visit(ASTNode.OrigD); + } + + return !shouldAbort(); +} + +bool CXIndexDataConsumer::handleModuleOccurence(const ImportDecl *ImportD, + SymbolRoleSet Roles, + FileID FID, + unsigned Offset) { + IndexingDeclVisitor(*this, SourceLocation(), nullptr).Visit(ImportD); + return !shouldAbort(); +} + +void CXIndexDataConsumer::finish() { + indexDiagnostics(); +} + + +CXIndexDataConsumer::ObjCProtocolListInfo::ObjCProtocolListInfo( const ObjCProtocolList &ProtList, - IndexingContext &IdxCtx, + CXIndexDataConsumer &IdxCtx, ScratchAlloc &SA) { ObjCInterfaceDecl::protocol_loc_iterator LI = ProtList.loc_begin(); for (ObjCInterfaceDecl::protocol_iterator @@ -61,7 +259,7 @@ IBOutletCollectionInfo::IBOutletCollectionInfo( IBCollInfo.objcClass = nullptr; } -AttrListInfo::AttrListInfo(const Decl *D, IndexingContext &IdxCtx) +AttrListInfo::AttrListInfo(const Decl *D, CXIndexDataConsumer &IdxCtx) : SA(IdxCtx), ref_cnt(0) { if (!D->hasAttrs()) @@ -114,14 +312,14 @@ AttrListInfo::AttrListInfo(const Decl *D, IndexingContext &IdxCtx) } IntrusiveRefCntPtr -AttrListInfo::create(const Decl *D, IndexingContext &IdxCtx) { +AttrListInfo::create(const Decl *D, CXIndexDataConsumer &IdxCtx) { ScratchAlloc SA(IdxCtx); AttrListInfo *attrs = SA.allocate(); return new (attrs) AttrListInfo(D, IdxCtx); } -IndexingContext::CXXBasesListInfo::CXXBasesListInfo(const CXXRecordDecl *D, - IndexingContext &IdxCtx, +CXIndexDataConsumer::CXXBasesListInfo::CXXBasesListInfo(const CXXRecordDecl *D, + CXIndexDataConsumer &IdxCtx, ScratchAlloc &SA) { for (const auto &Base : D->bases()) { BaseEntities.push_back(EntityInfo()); @@ -155,7 +353,7 @@ IndexingContext::CXXBasesListInfo::CXXBasesListInfo(const CXXRecordDecl *D, CXBases.push_back(&BaseInfos[i]); } -SourceLocation IndexingContext::CXXBasesListInfo::getBaseLoc( +SourceLocation CXIndexDataConsumer::CXXBasesListInfo::getBaseLoc( const CXXBaseSpecifier &Base) const { SourceLocation Loc = Base.getSourceRange().getBegin(); TypeLoc TL; @@ -193,16 +391,16 @@ const char *ScratchAlloc::copyCStr(StringRef Str) { return buf; } -void IndexingContext::setASTContext(ASTContext &ctx) { +void CXIndexDataConsumer::setASTContext(ASTContext &ctx) { Ctx = &ctx; cxtu::getASTUnit(CXTU)->setASTContext(&ctx); } -void IndexingContext::setPreprocessor(Preprocessor &PP) { +void CXIndexDataConsumer::setPreprocessor(Preprocessor &PP) { cxtu::getASTUnit(CXTU)->setPreprocessor(&PP); } -bool IndexingContext::isFunctionLocalDecl(const Decl *D) { +bool CXIndexDataConsumer::isFunctionLocalDecl(const Decl *D) { assert(D); if (!D->getParentFunctionOrMethod()) @@ -224,13 +422,13 @@ bool IndexingContext::isFunctionLocalDecl(const Decl *D) { return true; } -bool IndexingContext::shouldAbort() { +bool CXIndexDataConsumer::shouldAbort() { if (!CB.abortQuery) return false; return CB.abortQuery(ClientData, nullptr); } -void IndexingContext::enteredMainFile(const FileEntry *File) { +void CXIndexDataConsumer::enteredMainFile(const FileEntry *File) { if (File && CB.enteredMainFile) { CXIdxClientFile idxFile = CB.enteredMainFile(ClientData, @@ -240,7 +438,7 @@ void IndexingContext::enteredMainFile(const FileEntry *File) { } } -void IndexingContext::ppIncludedFile(SourceLocation hashLoc, +void CXIndexDataConsumer::ppIncludedFile(SourceLocation hashLoc, StringRef filename, const FileEntry *File, bool isImport, bool isAngled, @@ -258,7 +456,7 @@ void IndexingContext::ppIncludedFile(SourceLocation hashLoc, FileMap[File] = idxFile; } -void IndexingContext::importedModule(const ImportDecl *ImportD) { +void CXIndexDataConsumer::importedModule(const ImportDecl *ImportD) { if (!CB.importedASTFile) return; @@ -277,7 +475,7 @@ void IndexingContext::importedModule(const ImportDecl *ImportD) { (void)astFile; } -void IndexingContext::importedPCH(const FileEntry *File) { +void CXIndexDataConsumer::importedPCH(const FileEntry *File) { if (!CB.importedASTFile) return; @@ -292,21 +490,29 @@ void IndexingContext::importedPCH(const FileEntry *File) { (void)astFile; } -void IndexingContext::startedTranslationUnit() { +void CXIndexDataConsumer::startedTranslationUnit() { CXIdxClientContainer idxCont = nullptr; if (CB.startedTranslationUnit) idxCont = CB.startedTranslationUnit(ClientData, nullptr); addContainerInMap(Ctx->getTranslationUnitDecl(), idxCont); } -void IndexingContext::handleDiagnosticSet(CXDiagnostic CXDiagSet) { +void CXIndexDataConsumer::indexDiagnostics() { + if (!hasDiagnosticCallback()) + return; + + CXDiagnosticSetImpl *DiagSet = cxdiag::lazyCreateDiags(getCXTU()); + handleDiagnosticSet(DiagSet); +} + +void CXIndexDataConsumer::handleDiagnosticSet(CXDiagnostic CXDiagSet) { if (!CB.diagnostic) return; CB.diagnostic(ClientData, CXDiagSet, nullptr); } -bool IndexingContext::handleDecl(const NamedDecl *D, +bool CXIndexDataConsumer::handleDecl(const NamedDecl *D, SourceLocation Loc, CXCursor Cursor, DeclInfo &DInfo, const DeclContext *LexicalDC, @@ -365,14 +571,14 @@ bool IndexingContext::handleDecl(const NamedDecl *D, return true; } -bool IndexingContext::handleObjCContainer(const ObjCContainerDecl *D, +bool CXIndexDataConsumer::handleObjCContainer(const ObjCContainerDecl *D, SourceLocation Loc, CXCursor Cursor, ObjCContainerDeclInfo &ContDInfo) { ContDInfo.ObjCContDeclInfo.declInfo = &ContDInfo; return handleDecl(D, Loc, Cursor, ContDInfo); } -bool IndexingContext::handleFunction(const FunctionDecl *D) { +bool CXIndexDataConsumer::handleFunction(const FunctionDecl *D) { bool isDef = D->isThisDeclarationADefinition(); bool isContainer = isDef; bool isSkipped = false; @@ -388,31 +594,31 @@ bool IndexingContext::handleFunction(const FunctionDecl *D) { return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleVar(const VarDecl *D) { +bool CXIndexDataConsumer::handleVar(const VarDecl *D) { DeclInfo DInfo(!D->isFirstDecl(), D->isThisDeclarationADefinition(), /*isContainer=*/false); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleField(const FieldDecl *D) { +bool CXIndexDataConsumer::handleField(const FieldDecl *D) { DeclInfo DInfo(/*isRedeclaration=*/false, /*isDefinition=*/true, /*isContainer=*/false); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleMSProperty(const MSPropertyDecl *D) { +bool CXIndexDataConsumer::handleMSProperty(const MSPropertyDecl *D) { DeclInfo DInfo(/*isRedeclaration=*/false, /*isDefinition=*/true, /*isContainer=*/false); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleEnumerator(const EnumConstantDecl *D) { +bool CXIndexDataConsumer::handleEnumerator(const EnumConstantDecl *D) { DeclInfo DInfo(/*isRedeclaration=*/false, /*isDefinition=*/true, /*isContainer=*/false); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleTagDecl(const TagDecl *D) { +bool CXIndexDataConsumer::handleTagDecl(const TagDecl *D) { if (const CXXRecordDecl *CXXRD = dyn_cast(D)) return handleCXXRecordDecl(CXXRD, D); @@ -421,13 +627,13 @@ bool IndexingContext::handleTagDecl(const TagDecl *D) { return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleTypedefName(const TypedefNameDecl *D) { +bool CXIndexDataConsumer::handleTypedefName(const TypedefNameDecl *D) { DeclInfo DInfo(!D->isFirstDecl(), /*isDefinition=*/true, /*isContainer=*/false); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleObjCInterface(const ObjCInterfaceDecl *D) { +bool CXIndexDataConsumer::handleObjCInterface(const ObjCInterfaceDecl *D) { // For @class forward declarations, suppress them the same way as references. if (!D->isThisDeclarationADefinition()) { if (shouldSuppressRefs() && markEntityOccurrenceInFile(D, D->getLocation())) @@ -475,7 +681,7 @@ bool IndexingContext::handleObjCInterface(const ObjCInterfaceDecl *D) { return handleObjCContainer(D, D->getLocation(), getCursor(D), InterInfo); } -bool IndexingContext::handleObjCImplementation( +bool CXIndexDataConsumer::handleObjCImplementation( const ObjCImplementationDecl *D) { ObjCContainerDeclInfo ContDInfo(/*isForwardRef=*/false, /*isRedeclaration=*/true, @@ -483,7 +689,7 @@ bool IndexingContext::handleObjCImplementation( return handleObjCContainer(D, D->getLocation(), getCursor(D), ContDInfo); } -bool IndexingContext::handleObjCProtocol(const ObjCProtocolDecl *D) { +bool CXIndexDataConsumer::handleObjCProtocol(const ObjCProtocolDecl *D) { if (!D->isThisDeclarationADefinition()) { if (shouldSuppressRefs() && markEntityOccurrenceInFile(D, D->getLocation())) return false; // already occurred. @@ -512,7 +718,7 @@ bool IndexingContext::handleObjCProtocol(const ObjCProtocolDecl *D) { return handleObjCContainer(D, D->getLocation(), getCursor(D), ProtInfo); } -bool IndexingContext::handleObjCCategory(const ObjCCategoryDecl *D) { +bool CXIndexDataConsumer::handleObjCCategory(const ObjCCategoryDecl *D) { ScratchAlloc SA(*this); ObjCCategoryDeclInfo CatDInfo(/*isImplementation=*/false); @@ -544,7 +750,7 @@ bool IndexingContext::handleObjCCategory(const ObjCCategoryDecl *D) { return handleObjCContainer(D, CategoryLoc, getCursor(D), CatDInfo); } -bool IndexingContext::handleObjCCategoryImpl(const ObjCCategoryImplDecl *D) { +bool CXIndexDataConsumer::handleObjCCategoryImpl(const ObjCCategoryImplDecl *D) { ScratchAlloc SA(*this); const ObjCCategoryDecl *CatD = D->getCategoryDecl(); @@ -573,7 +779,7 @@ bool IndexingContext::handleObjCCategoryImpl(const ObjCCategoryImplDecl *D) { return handleObjCContainer(D, CategoryLoc, getCursor(D), CatDInfo); } -bool IndexingContext::handleObjCMethod(const ObjCMethodDecl *D) { +bool CXIndexDataConsumer::handleObjCMethod(const ObjCMethodDecl *D) { bool isDef = D->isThisDeclarationADefinition(); bool isContainer = isDef; bool isSkipped = false; @@ -589,7 +795,7 @@ bool IndexingContext::handleObjCMethod(const ObjCMethodDecl *D) { return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleSynthesizedObjCProperty( +bool CXIndexDataConsumer::handleSynthesizedObjCProperty( const ObjCPropertyImplDecl *D) { ObjCPropertyDecl *PD = D->getPropertyDecl(); auto *DC = D->getDeclContext(); @@ -597,7 +803,7 @@ bool IndexingContext::handleSynthesizedObjCProperty( dyn_cast(DC), DC); } -bool IndexingContext::handleSynthesizedObjCMethod(const ObjCMethodDecl *D, +bool CXIndexDataConsumer::handleSynthesizedObjCMethod(const ObjCMethodDecl *D, SourceLocation Loc, const DeclContext *LexicalDC) { DeclInfo DInfo(/*isRedeclaration=*/true, /*isDefinition=*/true, @@ -605,7 +811,7 @@ bool IndexingContext::handleSynthesizedObjCMethod(const ObjCMethodDecl *D, return handleDecl(D, Loc, getCursor(D), DInfo, LexicalDC, LexicalDC); } -bool IndexingContext::handleObjCProperty(const ObjCPropertyDecl *D) { +bool CXIndexDataConsumer::handleObjCProperty(const ObjCPropertyDecl *D) { ScratchAlloc SA(*this); ObjCPropertyDeclInfo DInfo; @@ -630,31 +836,31 @@ bool IndexingContext::handleObjCProperty(const ObjCPropertyDecl *D) { return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleNamespace(const NamespaceDecl *D) { +bool CXIndexDataConsumer::handleNamespace(const NamespaceDecl *D) { DeclInfo DInfo(/*isRedeclaration=*/!D->isOriginalNamespace(), /*isDefinition=*/true, /*isContainer=*/true); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleClassTemplate(const ClassTemplateDecl *D) { +bool CXIndexDataConsumer::handleClassTemplate(const ClassTemplateDecl *D) { return handleCXXRecordDecl(D->getTemplatedDecl(), D); } -bool IndexingContext::handleFunctionTemplate(const FunctionTemplateDecl *D) { +bool CXIndexDataConsumer::handleFunctionTemplate(const FunctionTemplateDecl *D) { DeclInfo DInfo(/*isRedeclaration=*/!D->isCanonicalDecl(), /*isDefinition=*/D->isThisDeclarationADefinition(), /*isContainer=*/D->isThisDeclarationADefinition()); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleTypeAliasTemplate(const TypeAliasTemplateDecl *D) { +bool CXIndexDataConsumer::handleTypeAliasTemplate(const TypeAliasTemplateDecl *D) { DeclInfo DInfo(/*isRedeclaration=*/!D->isCanonicalDecl(), /*isDefinition=*/true, /*isContainer=*/false); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleReference(const NamedDecl *D, SourceLocation Loc, +bool CXIndexDataConsumer::handleReference(const NamedDecl *D, SourceLocation Loc, const NamedDecl *Parent, const DeclContext *DC, const Expr *E, @@ -667,7 +873,7 @@ bool IndexingContext::handleReference(const NamedDecl *D, SourceLocation Loc, return handleReference(D, Loc, Cursor, Parent, DC, E, Kind); } -bool IndexingContext::handleReference(const NamedDecl *D, SourceLocation Loc, +bool CXIndexDataConsumer::handleReference(const NamedDecl *D, SourceLocation Loc, CXCursor Cursor, const NamedDecl *Parent, const DeclContext *DC, @@ -713,7 +919,7 @@ bool IndexingContext::handleReference(const NamedDecl *D, SourceLocation Loc, return true; } -bool IndexingContext::isNotFromSourceFile(SourceLocation Loc) const { +bool CXIndexDataConsumer::isNotFromSourceFile(SourceLocation Loc) const { if (Loc.isInvalid()) return true; SourceManager &SM = Ctx->getSourceManager(); @@ -722,7 +928,7 @@ bool IndexingContext::isNotFromSourceFile(SourceLocation Loc) const { return SM.getFileEntryForID(FID) == nullptr; } -void IndexingContext::addContainerInMap(const DeclContext *DC, +void CXIndexDataConsumer::addContainerInMap(const DeclContext *DC, CXIdxClientContainer container) { if (!DC) return; @@ -741,7 +947,7 @@ void IndexingContext::addContainerInMap(const DeclContext *DC, ContainerMap.erase(I); } -CXIdxClientEntity IndexingContext::getClientEntity(const Decl *D) const { +CXIdxClientEntity CXIndexDataConsumer::getClientEntity(const Decl *D) const { if (!D) return nullptr; EntityMapTy::const_iterator I = EntityMap.find(D); @@ -750,13 +956,13 @@ CXIdxClientEntity IndexingContext::getClientEntity(const Decl *D) const { return I->second; } -void IndexingContext::setClientEntity(const Decl *D, CXIdxClientEntity client) { +void CXIndexDataConsumer::setClientEntity(const Decl *D, CXIdxClientEntity client) { if (!D) return; EntityMap[D] = client; } -bool IndexingContext::handleCXXRecordDecl(const CXXRecordDecl *RD, +bool CXIndexDataConsumer::handleCXXRecordDecl(const CXXRecordDecl *RD, const NamedDecl *OrigD) { if (RD->isThisDeclarationADefinition()) { ScratchAlloc SA(*this); @@ -789,7 +995,7 @@ bool IndexingContext::handleCXXRecordDecl(const CXXRecordDecl *RD, return handleDecl(OrigD, OrigD->getLocation(), getCursor(OrigD), DInfo); } -bool IndexingContext::markEntityOccurrenceInFile(const NamedDecl *D, +bool CXIndexDataConsumer::markEntityOccurrenceInFile(const NamedDecl *D, SourceLocation Loc) { if (!D || Loc.isInvalid()) return true; @@ -811,7 +1017,7 @@ bool IndexingContext::markEntityOccurrenceInFile(const NamedDecl *D, return !res.second; // already in map } -const NamedDecl *IndexingContext::getEntityDecl(const NamedDecl *D) const { +const NamedDecl *CXIndexDataConsumer::getEntityDecl(const NamedDecl *D) const { assert(D); D = cast(D->getCanonicalDecl()); @@ -834,7 +1040,7 @@ const NamedDecl *IndexingContext::getEntityDecl(const NamedDecl *D) const { } const DeclContext * -IndexingContext::getEntityContainer(const Decl *D) const { +CXIndexDataConsumer::getEntityContainer(const Decl *D) const { const DeclContext *DC = dyn_cast(D); if (DC) return DC; @@ -850,7 +1056,7 @@ IndexingContext::getEntityContainer(const Decl *D) const { } CXIdxClientContainer -IndexingContext::getClientContainerForDC(const DeclContext *DC) const { +CXIndexDataConsumer::getClientContainerForDC(const DeclContext *DC) const { if (!DC) return nullptr; @@ -861,7 +1067,7 @@ IndexingContext::getClientContainerForDC(const DeclContext *DC) const { return I->second; } -CXIdxClientFile IndexingContext::getIndexFile(const FileEntry *File) { +CXIdxClientFile CXIndexDataConsumer::getIndexFile(const FileEntry *File) { if (!File) return nullptr; @@ -872,17 +1078,17 @@ CXIdxClientFile IndexingContext::getIndexFile(const FileEntry *File) { return nullptr; } -CXIdxLoc IndexingContext::getIndexLoc(SourceLocation Loc) const { +CXIdxLoc CXIndexDataConsumer::getIndexLoc(SourceLocation Loc) const { CXIdxLoc idxLoc = { {nullptr, nullptr}, 0 }; if (Loc.isInvalid()) return idxLoc; - idxLoc.ptr_data[0] = const_cast(this); + idxLoc.ptr_data[0] = const_cast(this); idxLoc.int_data = Loc.getRawEncoding(); return idxLoc; } -void IndexingContext::translateLoc(SourceLocation Loc, +void CXIndexDataConsumer::translateLoc(SourceLocation Loc, CXIdxClientFile *indexFile, CXFile *file, unsigned *line, unsigned *column, unsigned *offset) { @@ -912,7 +1118,12 @@ void IndexingContext::translateLoc(SourceLocation Loc, *offset = FileOffset; } -void IndexingContext::getEntityInfo(const NamedDecl *D, +static CXIdxEntityKind getEntityKindFromSymbolKind(SymbolKind K); +static CXIdxEntityCXXTemplateKind +getEntityKindFromSymbolCXXTemplateKind(SymbolCXXTemplateKind K); +static CXIdxEntityLanguage getEntityLangFromSymbolLang(SymbolLanguage L); + +void CXIndexDataConsumer::getEntityInfo(const NamedDecl *D, EntityInfo &EntityInfo, ScratchAlloc &SA) { if (!D) @@ -922,9 +1133,12 @@ void IndexingContext::getEntityInfo(const NamedDecl *D, EntityInfo.cursor = getCursor(D); EntityInfo.Dcl = D; EntityInfo.IndexCtx = this; - EntityInfo.kind = CXIdxEntity_Unexposed; - EntityInfo.templateKind = CXIdxEntity_NonTemplate; - EntityInfo.lang = CXIdxEntityLang_C; + + SymbolInfo SymInfo = getSymbolInfo(D); + EntityInfo.kind = getEntityKindFromSymbolKind(SymInfo.Kind); + EntityInfo.templateKind = + getEntityKindFromSymbolCXXTemplateKind(SymInfo.TemplateKind); + EntityInfo.lang = getEntityLangFromSymbolLang(SymInfo.Lang); if (D->hasAttrs()) { EntityInfo.AttrList = AttrListInfo::create(D, *this); @@ -932,167 +1146,9 @@ void IndexingContext::getEntityInfo(const NamedDecl *D, EntityInfo.numAttributes = EntityInfo.AttrList->getNumAttrs(); } - if (const TagDecl *TD = dyn_cast(D)) { - switch (TD->getTagKind()) { - case TTK_Struct: - EntityInfo.kind = CXIdxEntity_Struct; break; - case TTK_Union: - EntityInfo.kind = CXIdxEntity_Union; break; - case TTK_Class: - EntityInfo.kind = CXIdxEntity_CXXClass; - EntityInfo.lang = CXIdxEntityLang_CXX; - break; - case TTK_Interface: - EntityInfo.kind = CXIdxEntity_CXXInterface; - EntityInfo.lang = CXIdxEntityLang_CXX; - break; - case TTK_Enum: - EntityInfo.kind = CXIdxEntity_Enum; break; - } - - if (const CXXRecordDecl *CXXRec = dyn_cast(D)) - if (!CXXRec->isCLike()) - EntityInfo.lang = CXIdxEntityLang_CXX; - - if (isa(D)) { - EntityInfo.templateKind = CXIdxEntity_TemplatePartialSpecialization; - } else if (isa(D)) { - EntityInfo.templateKind = CXIdxEntity_TemplateSpecialization; - } - - } else { - switch (D->getKind()) { - case Decl::Typedef: - EntityInfo.kind = CXIdxEntity_Typedef; break; - case Decl::Function: - EntityInfo.kind = CXIdxEntity_Function; - break; - case Decl::ParmVar: - EntityInfo.kind = CXIdxEntity_Variable; - break; - case Decl::Var: - EntityInfo.kind = CXIdxEntity_Variable; - if (isa(D->getDeclContext())) { - EntityInfo.kind = CXIdxEntity_CXXStaticVariable; - EntityInfo.lang = CXIdxEntityLang_CXX; - } - break; - case Decl::Field: - EntityInfo.kind = CXIdxEntity_Field; - if (const CXXRecordDecl * - CXXRec = dyn_cast(D->getDeclContext())) { - // FIXME: isPOD check is not sufficient, a POD can contain methods, - // we want a isCStructLike check. - if (!CXXRec->isPOD()) - EntityInfo.lang = CXIdxEntityLang_CXX; - } - break; - case Decl::EnumConstant: - EntityInfo.kind = CXIdxEntity_EnumConstant; break; - case Decl::ObjCInterface: - EntityInfo.kind = CXIdxEntity_ObjCClass; - EntityInfo.lang = CXIdxEntityLang_ObjC; - break; - case Decl::ObjCProtocol: - EntityInfo.kind = CXIdxEntity_ObjCProtocol; - EntityInfo.lang = CXIdxEntityLang_ObjC; - break; - case Decl::ObjCCategory: - EntityInfo.kind = CXIdxEntity_ObjCCategory; - EntityInfo.lang = CXIdxEntityLang_ObjC; - break; - case Decl::ObjCMethod: - if (cast(D)->isInstanceMethod()) - EntityInfo.kind = CXIdxEntity_ObjCInstanceMethod; - else - EntityInfo.kind = CXIdxEntity_ObjCClassMethod; - EntityInfo.lang = CXIdxEntityLang_ObjC; - break; - case Decl::ObjCProperty: - EntityInfo.kind = CXIdxEntity_ObjCProperty; - EntityInfo.lang = CXIdxEntityLang_ObjC; - break; - case Decl::ObjCIvar: - EntityInfo.kind = CXIdxEntity_ObjCIvar; - EntityInfo.lang = CXIdxEntityLang_ObjC; - break; - case Decl::Namespace: - EntityInfo.kind = CXIdxEntity_CXXNamespace; - EntityInfo.lang = CXIdxEntityLang_CXX; - break; - case Decl::NamespaceAlias: - EntityInfo.kind = CXIdxEntity_CXXNamespaceAlias; - EntityInfo.lang = CXIdxEntityLang_CXX; - break; - case Decl::CXXConstructor: - EntityInfo.kind = CXIdxEntity_CXXConstructor; - EntityInfo.lang = CXIdxEntityLang_CXX; - break; - case Decl::CXXDestructor: - EntityInfo.kind = CXIdxEntity_CXXDestructor; - EntityInfo.lang = CXIdxEntityLang_CXX; - break; - case Decl::CXXConversion: - EntityInfo.kind = CXIdxEntity_CXXConversionFunction; - EntityInfo.lang = CXIdxEntityLang_CXX; - break; - case Decl::CXXMethod: { - const CXXMethodDecl *MD = cast(D); - if (MD->isStatic()) - EntityInfo.kind = CXIdxEntity_CXXStaticMethod; - else - EntityInfo.kind = CXIdxEntity_CXXInstanceMethod; - EntityInfo.lang = CXIdxEntityLang_CXX; - break; - } - case Decl::ClassTemplate: - EntityInfo.kind = CXIdxEntity_CXXClass; - EntityInfo.templateKind = CXIdxEntity_Template; - break; - case Decl::FunctionTemplate: - EntityInfo.kind = CXIdxEntity_Function; - EntityInfo.templateKind = CXIdxEntity_Template; - if (const CXXMethodDecl *MD = dyn_cast_or_null( - cast(D)->getTemplatedDecl())) { - if (isa(MD)) - EntityInfo.kind = CXIdxEntity_CXXConstructor; - else if (isa(MD)) - EntityInfo.kind = CXIdxEntity_CXXDestructor; - else if (isa(MD)) - EntityInfo.kind = CXIdxEntity_CXXConversionFunction; - else { - if (MD->isStatic()) - EntityInfo.kind = CXIdxEntity_CXXStaticMethod; - else - EntityInfo.kind = CXIdxEntity_CXXInstanceMethod; - } - } - break; - case Decl::TypeAliasTemplate: - EntityInfo.kind = CXIdxEntity_CXXTypeAlias; - EntityInfo.templateKind = CXIdxEntity_Template; - break; - case Decl::TypeAlias: - EntityInfo.kind = CXIdxEntity_CXXTypeAlias; - EntityInfo.lang = CXIdxEntityLang_CXX; - break; - default: - break; - } - } - if (EntityInfo.kind == CXIdxEntity_Unexposed) return; - if (const FunctionDecl *FD = dyn_cast(D)) { - if (FD->getTemplatedKind() == - FunctionDecl::TK_FunctionTemplateSpecialization) - EntityInfo.templateKind = CXIdxEntity_TemplateSpecialization; - } - - if (EntityInfo.templateKind != CXIdxEntity_NonTemplate) - EntityInfo.lang = CXIdxEntityLang_CXX; - if (IdentifierInfo *II = D->getIdentifier()) { EntityInfo.name = SA.toCStr(II->getName()); @@ -1119,14 +1175,14 @@ void IndexingContext::getEntityInfo(const NamedDecl *D, } } -void IndexingContext::getContainerInfo(const DeclContext *DC, +void CXIndexDataConsumer::getContainerInfo(const DeclContext *DC, ContainerInfo &ContInfo) { ContInfo.cursor = getCursor(cast(DC)); ContInfo.DC = DC; ContInfo.IndexCtx = this; } -CXCursor IndexingContext::getRefCursor(const NamedDecl *D, SourceLocation Loc) { +CXCursor CXIndexDataConsumer::getRefCursor(const NamedDecl *D, SourceLocation Loc) { if (const TypeDecl *TD = dyn_cast(D)) return MakeCursorTypeRef(TD, Loc, CXTU); if (const ObjCInterfaceDecl *ID = dyn_cast(D)) @@ -1147,7 +1203,7 @@ CXCursor IndexingContext::getRefCursor(const NamedDecl *D, SourceLocation Loc) { return clang_getNullCursor(); } -bool IndexingContext::shouldIgnoreIfImplicit(const Decl *D) { +bool CXIndexDataConsumer::shouldIgnoreIfImplicit(const Decl *D) { if (isa(D)) return false; if (isa(D)) @@ -1161,7 +1217,7 @@ bool IndexingContext::shouldIgnoreIfImplicit(const Decl *D) { return true; } -bool IndexingContext::isTemplateImplicitInstantiation(const Decl *D) { +bool CXIndexDataConsumer::isTemplateImplicitInstantiation(const Decl *D) { if (const ClassTemplateSpecializationDecl * SD = dyn_cast(D)) { return SD->getSpecializationKind() == TSK_ImplicitInstantiation; @@ -1171,3 +1227,60 @@ bool IndexingContext::isTemplateImplicitInstantiation(const Decl *D) { } return false; } + +static CXIdxEntityKind getEntityKindFromSymbolKind(SymbolKind K) { + switch (K) { + case SymbolKind::Unknown: + case SymbolKind::Module: + case SymbolKind::Macro: + return CXIdxEntity_Unexposed; + + case SymbolKind::Enum: return CXIdxEntity_Enum; + case SymbolKind::Struct: return CXIdxEntity_Struct; + case SymbolKind::Union: return CXIdxEntity_Union; + case SymbolKind::Typedef: return CXIdxEntity_Typedef; + case SymbolKind::Function: return CXIdxEntity_Function; + case SymbolKind::Variable: return CXIdxEntity_Variable; + case SymbolKind::Field: return CXIdxEntity_Field; + case SymbolKind::EnumConstant: return CXIdxEntity_EnumConstant; + case SymbolKind::ObjCClass: return CXIdxEntity_ObjCClass; + case SymbolKind::ObjCProtocol: return CXIdxEntity_ObjCProtocol; + case SymbolKind::ObjCCategory: return CXIdxEntity_ObjCCategory; + case SymbolKind::ObjCInstanceMethod: return CXIdxEntity_ObjCInstanceMethod; + case SymbolKind::ObjCClassMethod: return CXIdxEntity_ObjCClassMethod; + case SymbolKind::ObjCProperty: return CXIdxEntity_ObjCProperty; + case SymbolKind::ObjCIvar: return CXIdxEntity_ObjCIvar; + case SymbolKind::CXXClass: return CXIdxEntity_CXXClass; + case SymbolKind::CXXNamespace: return CXIdxEntity_CXXNamespace; + case SymbolKind::CXXNamespaceAlias: return CXIdxEntity_CXXNamespaceAlias; + case SymbolKind::CXXStaticVariable: return CXIdxEntity_CXXStaticVariable; + case SymbolKind::CXXStaticMethod: return CXIdxEntity_CXXStaticMethod; + case SymbolKind::CXXInstanceMethod: return CXIdxEntity_CXXInstanceMethod; + case SymbolKind::CXXConstructor: return CXIdxEntity_CXXConstructor; + case SymbolKind::CXXDestructor: return CXIdxEntity_CXXDestructor; + case SymbolKind::CXXConversionFunction: + return CXIdxEntity_CXXConversionFunction; + case SymbolKind::CXXTypeAlias: return CXIdxEntity_CXXTypeAlias; + case SymbolKind::CXXInterface: return CXIdxEntity_CXXInterface; + } +} + +static CXIdxEntityCXXTemplateKind +getEntityKindFromSymbolCXXTemplateKind(SymbolCXXTemplateKind K) { + switch (K) { + case SymbolCXXTemplateKind::NonTemplate: return CXIdxEntity_NonTemplate; + case SymbolCXXTemplateKind::Template: return CXIdxEntity_Template; + case SymbolCXXTemplateKind::TemplatePartialSpecialization: + return CXIdxEntity_TemplatePartialSpecialization; + case SymbolCXXTemplateKind::TemplateSpecialization: + return CXIdxEntity_TemplateSpecialization; + } +} + +static CXIdxEntityLanguage getEntityLangFromSymbolLang(SymbolLanguage L) { + switch (L) { + case SymbolLanguage::C: return CXIdxEntityLang_C; + case SymbolLanguage::ObjC: return CXIdxEntityLang_ObjC; + case SymbolLanguage::CXX: return CXIdxEntityLang_CXX; + } +} diff --git a/tools/libclang/IndexingContext.h b/tools/libclang/CXIndexDataConsumer.h similarity index 91% rename from tools/libclang/IndexingContext.h rename to tools/libclang/CXIndexDataConsumer.h index d1d62c90d45..308fa79488d 100644 --- a/tools/libclang/IndexingContext.h +++ b/tools/libclang/CXIndexDataConsumer.h @@ -1,4 +1,4 @@ -//===- IndexingContext.h - Higher level API functions -----------*- C++ -*-===// +//===- CXIndexDataConsumer.h - Index data consumer for libclang--*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -7,11 +7,12 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_TOOLS_LIBCLANG_INDEXINGCONTEXT_H -#define LLVM_CLANG_TOOLS_LIBCLANG_INDEXINGCONTEXT_H +#ifndef LLVM_CLANG_TOOLS_LIBCLANG_CXINDEXDATACONSUMER_H +#define LLVM_CLANG_TOOLS_LIBCLANG_CXINDEXDATACONSUMER_H #include "CXCursor.h" #include "Index_Internal.h" +#include "clang/Index/IndexDataConsumer.h" #include "clang/AST/DeclGroup.h" #include "clang/AST/DeclObjC.h" #include "llvm/ADT/DenseSet.h" @@ -27,14 +28,14 @@ namespace clang { class ClassTemplateSpecializationDecl; namespace cxindex { - class IndexingContext; + class CXIndexDataConsumer; class AttrListInfo; class ScratchAlloc { - IndexingContext &IdxCtx; + CXIndexDataConsumer &IdxCtx; public: - explicit ScratchAlloc(IndexingContext &indexCtx); + explicit ScratchAlloc(CXIndexDataConsumer &indexCtx); ScratchAlloc(const ScratchAlloc &SA); ~ScratchAlloc(); @@ -48,7 +49,7 @@ class ScratchAlloc { struct EntityInfo : public CXIdxEntityInfo { const NamedDecl *Dcl; - IndexingContext *IndexCtx; + CXIndexDataConsumer *IndexCtx; IntrusiveRefCntPtr AttrList; EntityInfo() { @@ -60,7 +61,7 @@ struct EntityInfo : public CXIdxEntityInfo { struct ContainerInfo : public CXIdxContainerInfo { const DeclContext *DC; - IndexingContext *IndexCtx; + CXIndexDataConsumer *IndexCtx; }; struct DeclInfo : public CXIdxDeclInfo { @@ -248,10 +249,10 @@ class AttrListInfo { AttrListInfo(const AttrListInfo &) = delete; void operator=(const AttrListInfo &) = delete; public: - AttrListInfo(const Decl *D, IndexingContext &IdxCtx); + AttrListInfo(const Decl *D, CXIndexDataConsumer &IdxCtx); static IntrusiveRefCntPtr create(const Decl *D, - IndexingContext &IdxCtx); + CXIndexDataConsumer &IdxCtx); const CXIdxAttrInfo *const *getAttrs() const { if (CXAttrs.empty()) @@ -273,7 +274,7 @@ class AttrListInfo { } }; -class IndexingContext { +class CXIndexDataConsumer : public index::IndexDataConsumer { ASTContext *Ctx; CXClientData ClientData; IndexerCallbacks &CB; @@ -308,7 +309,7 @@ class IndexingContext { } ObjCProtocolListInfo(const ObjCProtocolList &ProtList, - IndexingContext &IdxCtx, + CXIndexDataConsumer &IdxCtx, ScratchAlloc &SA); }; @@ -323,7 +324,7 @@ class IndexingContext { unsigned getNumBases() const { return (unsigned)CXBases.size(); } CXXBasesListInfo(const CXXRecordDecl *D, - IndexingContext &IdxCtx, ScratchAlloc &SA); + CXIndexDataConsumer &IdxCtx, ScratchAlloc &SA); private: SourceLocation getBaseLoc(const CXXBaseSpecifier &Base) const; @@ -332,13 +333,14 @@ class IndexingContext { friend class AttrListInfo; public: - IndexingContext(CXClientData clientData, IndexerCallbacks &indexCallbacks, + CXIndexDataConsumer(CXClientData clientData, IndexerCallbacks &indexCallbacks, unsigned indexOptions, CXTranslationUnit cxTU) : Ctx(nullptr), ClientData(clientData), CB(indexCallbacks), IndexOptions(indexOptions), CXTU(cxTU), StrScratch(), StrAdapterCount(0) { } ASTContext &getASTContext() const { return *Ctx; } + CXTranslationUnit getCXTU() const { return CXTU; } void setASTContext(ASTContext &ctx); void setPreprocessor(Preprocessor &PP); @@ -391,6 +393,8 @@ class IndexingContext { void indexBody(const Stmt *S, const NamedDecl *Parent, const DeclContext *DC = nullptr); + void indexDiagnostics(); + void handleDiagnosticSet(CXDiagnosticSet CXDiagSet); bool handleFunction(const FunctionDecl *FD); @@ -458,6 +462,17 @@ class IndexingContext { static bool isTemplateImplicitInstantiation(const Decl *D); private: + bool handleDeclOccurence(const Decl *D, index::SymbolRoleSet Roles, + ArrayRef Relations, + FileID FID, unsigned Offset, + ASTNodeInfo ASTNode) override; + + bool handleModuleOccurence(const ImportDecl *ImportD, + index::SymbolRoleSet Roles, + FileID FID, unsigned Offset) override; + + void finish() override; + bool handleDecl(const NamedDecl *D, SourceLocation Loc, CXCursor Cursor, DeclInfo &DInfo, @@ -495,7 +510,7 @@ class IndexingContext { static bool shouldIgnoreIfImplicit(const Decl *D); }; -inline ScratchAlloc::ScratchAlloc(IndexingContext &idxCtx) : IdxCtx(idxCtx) { +inline ScratchAlloc::ScratchAlloc(CXIndexDataConsumer &idxCtx) : IdxCtx(idxCtx) { ++IdxCtx.StrAdapterCount; } inline ScratchAlloc::ScratchAlloc(const ScratchAlloc &SA) : IdxCtx(SA.IdxCtx) { diff --git a/tools/libclang/IndexBody.cpp b/tools/libclang/IndexBody.cpp deleted file mode 100644 index 58dc11722bf..00000000000 --- a/tools/libclang/IndexBody.cpp +++ /dev/null @@ -1,224 +0,0 @@ -//===- CIndexHigh.cpp - Higher level API functions ------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "IndexingContext.h" -#include "clang/AST/RecursiveASTVisitor.h" - -using namespace clang; -using namespace cxindex; - -namespace { - -class BodyIndexer : public RecursiveASTVisitor { - IndexingContext &IndexCtx; - const NamedDecl *Parent; - const DeclContext *ParentDC; - - typedef RecursiveASTVisitor base; -public: - BodyIndexer(IndexingContext &indexCtx, - const NamedDecl *Parent, const DeclContext *DC) - : IndexCtx(indexCtx), Parent(Parent), ParentDC(DC) { } - - bool shouldWalkTypesOfTypeLocs() const { return false; } - - bool TraverseTypeLoc(TypeLoc TL) { - IndexCtx.indexTypeLoc(TL, Parent, ParentDC); - return true; - } - - bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) { - IndexCtx.indexNestedNameSpecifierLoc(NNS, Parent, ParentDC); - return true; - } - - bool VisitDeclRefExpr(DeclRefExpr *E) { - IndexCtx.handleReference(E->getDecl(), E->getLocation(), - Parent, ParentDC, E); - return true; - } - - bool VisitMemberExpr(MemberExpr *E) { - IndexCtx.handleReference(E->getMemberDecl(), E->getMemberLoc(), - Parent, ParentDC, E); - return true; - } - - bool VisitObjCIvarRefExpr(ObjCIvarRefExpr *E) { - IndexCtx.handleReference(E->getDecl(), E->getLocation(), - Parent, ParentDC, E); - return true; - } - - bool VisitObjCMessageExpr(ObjCMessageExpr *E) { - if (ObjCMethodDecl *MD = E->getMethodDecl()) - IndexCtx.handleReference(MD, E->getSelectorStartLoc(), - Parent, ParentDC, E, - E->isImplicit() ? CXIdxEntityRef_Implicit - : CXIdxEntityRef_Direct); - return true; - } - - bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *E) { - if (E->isExplicitProperty()) - IndexCtx.handleReference(E->getExplicitProperty(), E->getLocation(), - Parent, ParentDC, E); - - // No need to do a handleReference for the objc method, because there will - // be a message expr as part of PseudoObjectExpr. - return true; - } - - bool VisitMSPropertyRefExpr(MSPropertyRefExpr *E) { - IndexCtx.handleReference(E->getPropertyDecl(), E->getMemberLoc(), Parent, - ParentDC, E, CXIdxEntityRef_Direct); - return true; - } - - bool VisitObjCProtocolExpr(ObjCProtocolExpr *E) { - IndexCtx.handleReference(E->getProtocol(), E->getProtocolIdLoc(), - Parent, ParentDC, E, CXIdxEntityRef_Direct); - return true; - } - - bool VisitObjCBoxedExpr(ObjCBoxedExpr *E) { - if (ObjCMethodDecl *MD = E->getBoxingMethod()) - IndexCtx.handleReference(MD, E->getLocStart(), - Parent, ParentDC, E, CXIdxEntityRef_Implicit); - return true; - } - - bool VisitObjCDictionaryLiteral(ObjCDictionaryLiteral *E) { - if (ObjCMethodDecl *MD = E->getDictWithObjectsMethod()) - IndexCtx.handleReference(MD, E->getLocStart(), - Parent, ParentDC, E, CXIdxEntityRef_Implicit); - return true; - } - - bool VisitObjCArrayLiteral(ObjCArrayLiteral *E) { - if (ObjCMethodDecl *MD = E->getArrayWithObjectsMethod()) - IndexCtx.handleReference(MD, E->getLocStart(), - Parent, ParentDC, E, CXIdxEntityRef_Implicit); - return true; - } - - bool VisitCXXConstructExpr(CXXConstructExpr *E) { - IndexCtx.handleReference(E->getConstructor(), E->getLocation(), - Parent, ParentDC, E); - return true; - } - - bool TraverseCXXOperatorCallExpr(CXXOperatorCallExpr *E, - DataRecursionQueue *Q = nullptr) { - if (E->getOperatorLoc().isInvalid()) - return true; // implicit. - return base::TraverseCXXOperatorCallExpr(E, Q); - } - - bool VisitDeclStmt(DeclStmt *S) { - if (IndexCtx.shouldIndexFunctionLocalSymbols()) { - IndexCtx.indexDeclGroupRef(S->getDeclGroup()); - return true; - } - - DeclGroupRef DG = S->getDeclGroup(); - for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) { - const Decl *D = *I; - if (!D) - continue; - if (!IndexCtx.isFunctionLocalDecl(D)) - IndexCtx.indexTopLevelDecl(D); - } - - return true; - } - - bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C) { - if (C->capturesThis() || C->capturesVLAType()) - return true; - - if (C->capturesVariable() && IndexCtx.shouldIndexFunctionLocalSymbols()) - IndexCtx.handleReference(C->getCapturedVar(), C->getLocation(), Parent, - ParentDC); - - // FIXME: Lambda init-captures. - return true; - } - - // RecursiveASTVisitor visits both syntactic and semantic forms, duplicating - // the things that we visit. Make sure to only visit the semantic form. - // Also visit things that are in the syntactic form but not the semantic one, - // for example the indices in DesignatedInitExprs. - bool TraverseInitListExpr(InitListExpr *S) { - - class SyntacticFormIndexer : - public RecursiveASTVisitor { - IndexingContext &IndexCtx; - const NamedDecl *Parent; - const DeclContext *ParentDC; - - public: - SyntacticFormIndexer(IndexingContext &indexCtx, - const NamedDecl *Parent, const DeclContext *DC) - : IndexCtx(indexCtx), Parent(Parent), ParentDC(DC) { } - - bool shouldWalkTypesOfTypeLocs() const { return false; } - - bool VisitDesignatedInitExpr(DesignatedInitExpr *E) { - for (DesignatedInitExpr::reverse_designators_iterator - D = E->designators_rbegin(), DEnd = E->designators_rend(); - D != DEnd; ++D) { - if (D->isFieldDesignator()) - IndexCtx.handleReference(D->getField(), D->getFieldLoc(), - Parent, ParentDC, E); - } - return true; - } - }; - - auto visitForm = [&](InitListExpr *Form) { - for (Stmt *SubStmt : Form->children()) { - if (!TraverseStmt(SubStmt)) - return false; - } - return true; - }; - - InitListExpr *SemaForm = S->isSemanticForm() ? S : S->getSemanticForm(); - InitListExpr *SyntaxForm = S->isSemanticForm() ? S->getSyntacticForm() : S; - - if (SemaForm) { - // Visit things present in syntactic form but not the semantic form. - if (SyntaxForm) { - SyntacticFormIndexer(IndexCtx, Parent, ParentDC).TraverseStmt(SyntaxForm); - } - return visitForm(SemaForm); - } - - // No semantic, try the syntactic. - if (SyntaxForm) { - return visitForm(SyntaxForm); - } - - return true; - } - -}; - -} // anonymous namespace - -void IndexingContext::indexBody(const Stmt *S, const NamedDecl *Parent, - const DeclContext *DC) { - if (!S) - return; - - if (!DC) - DC = Parent->getLexicalDeclContext(); - BodyIndexer(*this, Parent, DC).TraverseStmt(const_cast(S)); -} diff --git a/tools/libclang/Indexing.cpp b/tools/libclang/Indexing.cpp index 4929d6244e5..b37a8864dd6 100644 --- a/tools/libclang/Indexing.cpp +++ b/tools/libclang/Indexing.cpp @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// -#include "IndexingContext.h" +#include "CXIndexDataConsumer.h" #include "CIndexDiagnostic.h" #include "CIndexer.h" #include "CLog.h" @@ -16,12 +16,12 @@ #include "CXString.h" #include "CXTranslationUnit.h" #include "clang/AST/ASTConsumer.h" -#include "clang/AST/DeclVisitor.h" #include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/FrontendAction.h" #include "clang/Frontend/Utils.h" +#include "clang/Index/IndexingAction.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/PPCallbacks.h" #include "clang/Lex/PPConditionalDirectiveRecord.h" @@ -34,11 +34,10 @@ #include using namespace clang; +using namespace clang::index; using namespace cxtu; using namespace cxindex; -static void indexDiagnostics(CXTranslationUnit TU, IndexingContext &IdxCtx); - namespace { //===----------------------------------------------------------------------===// @@ -246,12 +245,12 @@ class TUSkipBodyControl { class IndexPPCallbacks : public PPCallbacks { Preprocessor &PP; - IndexingContext &IndexCtx; + CXIndexDataConsumer &DataConsumer; bool IsMainFileEntered; public: - IndexPPCallbacks(Preprocessor &PP, IndexingContext &indexCtx) - : PP(PP), IndexCtx(indexCtx), IsMainFileEntered(false) { } + IndexPPCallbacks(Preprocessor &PP, CXIndexDataConsumer &dataConsumer) + : PP(PP), DataConsumer(dataConsumer), IsMainFileEntered(false) { } void FileChanged(SourceLocation Loc, FileChangeReason Reason, SrcMgr::CharacteristicKind FileType, FileID PrevFID) override { @@ -263,7 +262,7 @@ class IndexPPCallbacks : public PPCallbacks { if (Loc == MainFileLoc && Reason == PPCallbacks::EnterFile) { IsMainFileEntered = true; - IndexCtx.enteredMainFile(SM.getFileEntryForID(SM.getMainFileID())); + DataConsumer.enteredMainFile(SM.getFileEntryForID(SM.getMainFileID())); } } @@ -274,7 +273,7 @@ class IndexPPCallbacks : public PPCallbacks { const Module *Imported) override { bool isImport = (IncludeTok.is(tok::identifier) && IncludeTok.getIdentifierInfo()->getPPKeywordID() == tok::pp_import); - IndexCtx.ppIncludedFile(HashLoc, FileName, File, isImport, IsAngled, + DataConsumer.ppIncludedFile(HashLoc, FileName, File, isImport, IsAngled, Imported); } @@ -301,18 +300,18 @@ class IndexPPCallbacks : public PPCallbacks { //===----------------------------------------------------------------------===// class IndexingConsumer : public ASTConsumer { - IndexingContext &IndexCtx; + CXIndexDataConsumer &DataConsumer; TUSkipBodyControl *SKCtrl; public: - IndexingConsumer(IndexingContext &indexCtx, TUSkipBodyControl *skCtrl) - : IndexCtx(indexCtx), SKCtrl(skCtrl) { } + IndexingConsumer(CXIndexDataConsumer &dataConsumer, TUSkipBodyControl *skCtrl) + : DataConsumer(dataConsumer), SKCtrl(skCtrl) { } // ASTConsumer Implementation void Initialize(ASTContext &Context) override { - IndexCtx.setASTContext(Context); - IndexCtx.startedTranslationUnit(); + DataConsumer.setASTContext(Context); + DataConsumer.startedTranslationUnit(); } void HandleTranslationUnit(ASTContext &Ctx) override { @@ -321,34 +320,7 @@ class IndexingConsumer : public ASTConsumer { } bool HandleTopLevelDecl(DeclGroupRef DG) override { - IndexCtx.indexDeclGroupRef(DG); - return !IndexCtx.shouldAbort(); - } - - /// \brief Handle the specified top-level declaration that occurred inside - /// and ObjC container. - void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) override { - IndexCtx.indexDeclGroupRef(DG); - } - - /// \brief This is called by the AST reader when deserializing things. - /// The default implementation forwards to HandleTopLevelDecl but we don't - /// care about them when indexing, so have an empty definition. - void HandleInterestingDecl(DeclGroupRef D) override {} - - void HandleTagDeclDefinition(TagDecl *D) override { - if (!IndexCtx.shouldIndexImplicitTemplateInsts()) - return; - - if (IndexCtx.isTemplateImplicitInstantiation(D)) - IndexCtx.indexDecl(D); - } - - void HandleCXXImplicitFunctionInstantiation(FunctionDecl *D) override { - if (!IndexCtx.shouldIndexImplicitTemplateInsts()) - return; - - IndexCtx.indexDecl(D); + return !DataConsumer.shouldAbort(); } bool shouldSkipFunctionBody(Decl *D) override { @@ -357,7 +329,7 @@ class IndexingConsumer : public ASTConsumer { return true; } - const SourceManager &SM = IndexCtx.getASTContext().getSourceManager(); + const SourceManager &SM = DataConsumer.getASTContext().getSourceManager(); SourceLocation Loc = D->getLocation(); if (Loc.isMacroID()) return false; @@ -398,34 +370,29 @@ class CaptureDiagnosticConsumer : public DiagnosticConsumer { //===----------------------------------------------------------------------===// class IndexingFrontendAction : public ASTFrontendAction { - IndexingContext IndexCtx; - CXTranslationUnit CXTU; + std::shared_ptr DataConsumer; SessionSkipBodyData *SKData; std::unique_ptr SKCtrl; public: - IndexingFrontendAction(CXClientData clientData, - IndexerCallbacks &indexCallbacks, - unsigned indexOptions, - CXTranslationUnit cxTU, + IndexingFrontendAction(std::shared_ptr dataConsumer, SessionSkipBodyData *skData) - : IndexCtx(clientData, indexCallbacks, indexOptions, cxTU), - CXTU(cxTU), SKData(skData) { } + : DataConsumer(dataConsumer), SKData(skData) { } std::unique_ptr CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override { PreprocessorOptions &PPOpts = CI.getPreprocessorOpts(); if (!PPOpts.ImplicitPCHInclude.empty()) { - IndexCtx.importedPCH( + DataConsumer->importedPCH( CI.getFileManager().getFile(PPOpts.ImplicitPCHInclude)); } - IndexCtx.setASTContext(CI.getASTContext()); + DataConsumer->setASTContext(CI.getASTContext()); Preprocessor &PP = CI.getPreprocessor(); - PP.addPPCallbacks(llvm::make_unique(PP, IndexCtx)); - IndexCtx.setPreprocessor(PP); + PP.addPPCallbacks(llvm::make_unique(PP, *DataConsumer)); + DataConsumer->setPreprocessor(PP); if (SKData) { auto *PPRec = new PPConditionalDirectiveRecord(PP.getSourceManager()); @@ -433,15 +400,11 @@ class IndexingFrontendAction : public ASTFrontendAction { SKCtrl = llvm::make_unique(*SKData, *PPRec, PP); } - return llvm::make_unique(IndexCtx, SKCtrl.get()); - } - - void EndSourceFileAction() override { - indexDiagnostics(CXTU, IndexCtx); + return llvm::make_unique(*DataConsumer, SKCtrl.get()); } TranslationUnitKind getTranslationUnitKind() override { - if (IndexCtx.shouldIndexImplicitTemplateInsts()) + if (DataConsumer->shouldIndexImplicitTemplateInsts()) return TU_Complete; else return TU_Prefix; @@ -453,6 +416,13 @@ class IndexingFrontendAction : public ASTFrontendAction { // clang_indexSourceFileUnit Implementation //===----------------------------------------------------------------------===// +static IndexingOptions getIndexingOptionsFromCXOptions(unsigned index_options) { + IndexingOptions IdxOpts; + if (index_options & CXIndexOpt_IndexFunctionLocalSymbols) + IdxOpts.IndexFunctionLocals = true; + return IdxOpts; +} + struct IndexSessionData { CXIndex CIdx; std::unique_ptr SkipBodyData; @@ -588,13 +558,17 @@ static CXErrorCode clang_indexSourceFile_Impl( if (SkipBodies) CInvok->getFrontendOpts().SkipFunctionBodies = true; - std::unique_ptr IndexAction; - IndexAction.reset(new IndexingFrontendAction(client_data, CB, - index_options, CXTU->getTU(), - SkipBodies ? IdxSession->SkipBodyData.get() : nullptr)); + auto DataConsumer = + std::make_shared(client_data, CB, index_options, + CXTU->getTU()); + auto InterAction = llvm::make_unique(DataConsumer, + SkipBodies ? IdxSession->SkipBodyData.get() : nullptr); + std::unique_ptr IndexAction; + IndexAction = createIndexingAction(std::move(InterAction), DataConsumer, + getIndexingOptionsFromCXOptions(index_options)); // Recover resources if we crash before exiting this method. - llvm::CrashRecoveryContextCleanupRegistrar + llvm::CrashRecoveryContextCleanupRegistrar IndexActionCleanup(IndexAction.get()); bool Persistent = requestedToGetTU; @@ -654,7 +628,7 @@ static CXErrorCode clang_indexSourceFile_Impl( // clang_indexTranslationUnit Implementation //===----------------------------------------------------------------------===// -static void indexPreprocessingRecord(ASTUnit &Unit, IndexingContext &IdxCtx) { +static void indexPreprocessingRecord(ASTUnit &Unit, CXIndexDataConsumer &IdxCtx) { Preprocessor &PP = Unit.getPreprocessor(); if (!PP.getPreprocessingRecord()) return; @@ -677,24 +651,6 @@ static void indexPreprocessingRecord(ASTUnit &Unit, IndexingContext &IdxCtx) { } } -static bool topLevelDeclVisitor(void *context, const Decl *D) { - IndexingContext &IdxCtx = *static_cast(context); - IdxCtx.indexTopLevelDecl(D); - return !IdxCtx.shouldAbort(); -} - -static void indexTranslationUnit(ASTUnit &Unit, IndexingContext &IdxCtx) { - Unit.visitLocalTopLevelDecls(&IdxCtx, topLevelDeclVisitor); -} - -static void indexDiagnostics(CXTranslationUnit TU, IndexingContext &IdxCtx) { - if (!IdxCtx.hasDiagnosticCallback()) - return; - - CXDiagnosticSetImpl *DiagSet = cxdiag::lazyCreateDiags(TU); - IdxCtx.handleDiagnosticSet(DiagSet); -} - static CXErrorCode clang_indexTranslationUnit_Impl( CXIndexAction idxAction, CXClientData client_data, IndexerCallbacks *client_index_callbacks, unsigned index_callbacks_size, @@ -718,19 +674,8 @@ static CXErrorCode clang_indexTranslationUnit_Impl( ? index_callbacks_size : sizeof(CB); memcpy(&CB, client_index_callbacks, ClientCBSize); - std::unique_ptr IndexCtx; - IndexCtx.reset(new IndexingContext(client_data, CB, index_options, TU)); - - // Recover resources if we crash before exiting this method. - llvm::CrashRecoveryContextCleanupRegistrar - IndexCtxCleanup(IndexCtx.get()); - - std::unique_ptr IndexConsumer; - IndexConsumer.reset(new IndexingConsumer(*IndexCtx, nullptr)); - - // Recover resources if we crash before exiting this method. - llvm::CrashRecoveryContextCleanupRegistrar - IndexConsumerCleanup(IndexConsumer.get()); + auto DataConsumer = std::make_shared(client_data, CB, + index_options, TU); ASTUnit *Unit = cxtu::getASTUnit(TU); if (!Unit) @@ -739,20 +684,21 @@ static CXErrorCode clang_indexTranslationUnit_Impl( ASTUnit::ConcurrencyCheck Check(*Unit); if (const FileEntry *PCHFile = Unit->getPCHFile()) - IndexCtx->importedPCH(PCHFile); + DataConsumer->importedPCH(PCHFile); FileManager &FileMgr = Unit->getFileManager(); if (Unit->getOriginalSourceFileName().empty()) - IndexCtx->enteredMainFile(nullptr); + DataConsumer->enteredMainFile(nullptr); else - IndexCtx->enteredMainFile(FileMgr.getFile(Unit->getOriginalSourceFileName())); + DataConsumer->enteredMainFile(FileMgr.getFile(Unit->getOriginalSourceFileName())); - IndexConsumer->Initialize(Unit->getASTContext()); + DataConsumer->setASTContext(Unit->getASTContext()); + DataConsumer->startedTranslationUnit(); - indexPreprocessingRecord(*Unit, *IndexCtx); - indexTranslationUnit(*Unit, *IndexCtx); - indexDiagnostics(TU, *IndexCtx); + indexPreprocessingRecord(*Unit, *DataConsumer); + indexASTUnit(*Unit, DataConsumer, getIndexingOptionsFromCXOptions(index_options)); + DataConsumer->indexDiagnostics(); return CXError_Success; } @@ -1037,9 +983,9 @@ void clang_indexLoc_getFileLocation(CXIdxLoc location, if (!location.ptr_data[0] || Loc.isInvalid()) return; - IndexingContext &IndexCtx = - *static_cast(location.ptr_data[0]); - IndexCtx.translateLoc(Loc, indexFile, file, line, column, offset); + CXIndexDataConsumer &DataConsumer = + *static_cast(location.ptr_data[0]); + DataConsumer.translateLoc(Loc, indexFile, file, line, column, offset); } CXSourceLocation clang_indexLoc_getCXSourceLocation(CXIdxLoc location) { @@ -1047,9 +993,9 @@ CXSourceLocation clang_indexLoc_getCXSourceLocation(CXIdxLoc location) { if (!location.ptr_data[0] || Loc.isInvalid()) return clang_getNullLocation(); - IndexingContext &IndexCtx = - *static_cast(location.ptr_data[0]); - return cxloc::translateSourceLocation(IndexCtx.getASTContext(), Loc); + CXIndexDataConsumer &DataConsumer = + *static_cast(location.ptr_data[0]); + return cxloc::translateSourceLocation(DataConsumer.getASTContext(), Loc); } } // end: extern "C" From 0038d42b6f712d4762ae1d5dbcd431aa14772f83 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Fri, 12 Feb 2016 23:30:07 +0000 Subject: [PATCH 144/742] [index] Add llvm/Support/DataTypes.h header to fix build failures in the bots. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260762 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Index/IndexSymbol.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/clang/Index/IndexSymbol.h b/include/clang/Index/IndexSymbol.h index 0849241b60e..7a505eac415 100644 --- a/include/clang/Index/IndexSymbol.h +++ b/include/clang/Index/IndexSymbol.h @@ -11,6 +11,7 @@ #define LLVM_CLANG_INDEX_INDEXSYMBOL_H #include "clang/Basic/LLVM.h" +#include "llvm/Support/DataTypes.h" namespace clang { class Decl; From 7d0dcee2e02c9c2eb46ccad27653c579fc177907 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Sat, 13 Feb 2016 01:24:19 +0000 Subject: [PATCH 145/742] [RecursiveASTVisitor] Introduce dataTraverseStmtPre()/dataTraverseStmtPost() to allow clients to do before/after actions during data recursive visitation. This should fix the asan bot that hits stack overflow in a couple of test/Index tests. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260785 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/RecursiveASTVisitor.h | 17 ++++++++++++++++- lib/Index/IndexBody.cpp | 10 +++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h index f1ae76b59c2..42e67517a61 100644 --- a/include/clang/AST/RecursiveASTVisitor.h +++ b/include/clang/AST/RecursiveASTVisitor.h @@ -163,6 +163,18 @@ template class RecursiveASTVisitor { /// otherwise (including when the argument is nullptr). bool TraverseStmt(Stmt *S, DataRecursionQueue *Queue = nullptr); + /// Invoked before visiting a statement or expression via data recursion. + /// + /// \returns false to skip visiting the node, true otherwise. + bool dataTraverseStmtPre(Stmt *S) { return true; } + + /// Invoked after visiting a statement or expression via data recursion. + /// This is not invoked if the previously invoked \c dataTraverseStmtPre + /// returned false. + /// + /// \returns false if the visitation was terminated early, true otherwise. + bool dataTraverseStmtPost(Stmt *S) { return true; } + /// \brief Recursively visit a type, by dispatching to /// Traverse*Type() based on the argument's getTypeClass() property. /// @@ -557,7 +569,10 @@ bool RecursiveASTVisitor::TraverseStmt(Stmt *S, Stmt *CurrS = LocalQueue.pop_back_val(); size_t N = LocalQueue.size(); - TRY_TO(dataTraverseNode(CurrS, &LocalQueue)); + if (getDerived().dataTraverseStmtPre(CurrS)) { + TRY_TO(dataTraverseNode(CurrS, &LocalQueue)); + TRY_TO(dataTraverseStmtPost(CurrS)); + } // Process new children in the order they were added. std::reverse(LocalQueue.begin() + N, LocalQueue.end()); } diff --git a/lib/Index/IndexBody.cpp b/lib/Index/IndexBody.cpp index 56fba28387d..f7164453db6 100644 --- a/lib/Index/IndexBody.cpp +++ b/lib/Index/IndexBody.cpp @@ -29,11 +29,15 @@ class BodyIndexer : public RecursiveASTVisitor { bool shouldWalkTypesOfTypeLocs() const { return false; } - bool TraverseStmt(Stmt *S) { + bool dataTraverseStmtPre(Stmt *S) { StmtStack.push_back(S); - bool ret = base::TraverseStmt(S); + return true; + } + + bool dataTraverseStmtPost(Stmt *S) { + assert(StmtStack.back() == S); StmtStack.pop_back(); - return ret; + return true; } bool TraverseTypeLoc(TypeLoc TL) { From 7afb4e911be4d0524bf776a307633b6d5d51383f Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Sat, 13 Feb 2016 05:17:15 +0000 Subject: [PATCH 146/742] [index] Change some default parameters to fix an MSVC ICE. Many thanks to Yunzhong Gao for tracking this down! git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260807 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Index/IndexingContext.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Index/IndexingContext.h b/lib/Index/IndexingContext.h index 774650547f8..600fc433b58 100644 --- a/lib/Index/IndexingContext.h +++ b/lib/Index/IndexingContext.h @@ -62,18 +62,18 @@ class IndexingContext { static bool isTemplateImplicitInstantiation(const Decl *D); bool handleDecl(const Decl *D, SymbolRoleSet Roles = SymbolRoleSet(), - ArrayRef Relations = {}); + ArrayRef Relations = None); bool handleDecl(const Decl *D, SourceLocation Loc, SymbolRoleSet Roles = SymbolRoleSet(), - ArrayRef Relations = {}, + ArrayRef Relations = None, const DeclContext *DC = nullptr); bool handleReference(const NamedDecl *D, SourceLocation Loc, const NamedDecl *Parent, const DeclContext *DC, SymbolRoleSet Roles, - ArrayRef Relations = {}, + ArrayRef Relations = None, const Expr *RefE = nullptr, const Decl *RefD = nullptr); From e45fe7861c886c8b2a0a80e450e107929c37211c Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Sat, 13 Feb 2016 21:46:50 +0000 Subject: [PATCH 147/742] [AST] Add a print() method in DeclarationName that accepts a PrintingPolicy. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260833 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/DeclarationName.h | 5 +- lib/AST/DeclarationName.cpp | 76 +++++++++++++++++++---------- 2 files changed, 54 insertions(+), 27 deletions(-) diff --git a/include/clang/AST/DeclarationName.h b/include/clang/AST/DeclarationName.h index 9482e83e81d..2d3cfe27a16 100644 --- a/include/clang/AST/DeclarationName.h +++ b/include/clang/AST/DeclarationName.h @@ -30,6 +30,7 @@ namespace clang { class IdentifierInfo; class MultiKeywordSelector; enum OverloadedOperatorKind : int; + struct PrintingPolicy; class QualType; class Type; class TypeSourceInfo; @@ -302,7 +303,9 @@ class DeclarationName { } static int compare(DeclarationName LHS, DeclarationName RHS); - + + void print(raw_ostream &OS, const PrintingPolicy &Policy); + void dump() const; }; diff --git a/lib/AST/DeclarationName.cpp b/lib/AST/DeclarationName.cpp index 8322b607a6c..f6d045b2dab 100644 --- a/lib/AST/DeclarationName.cpp +++ b/lib/AST/DeclarationName.cpp @@ -133,36 +133,43 @@ int DeclarationName::compare(DeclarationName LHS, DeclarationName RHS) { llvm_unreachable("Invalid DeclarationName Kind!"); } -raw_ostream &operator<<(raw_ostream &OS, DeclarationName N) { +static void printCXXConstructorDestructorName(QualType ClassType, + raw_ostream &OS, + const PrintingPolicy &Policy) { + if (const RecordType *ClassRec = ClassType->getAs()) { + OS << *ClassRec->getDecl(); + return; + } + if (!Policy.LangOpts.CPlusPlus) { + // Passed policy is the default one from operator <<, use a C++ policy. + LangOptions LO; + LO.CPlusPlus = true; + ClassType.print(OS, PrintingPolicy(LO)); + } else { + ClassType.print(OS, Policy); + } +} + +void DeclarationName::print(raw_ostream &OS, const PrintingPolicy &Policy) { + DeclarationName &N = *this; switch (N.getNameKind()) { case DeclarationName::Identifier: if (const IdentifierInfo *II = N.getAsIdentifierInfo()) OS << II->getName(); - return OS; + return; case DeclarationName::ObjCZeroArgSelector: case DeclarationName::ObjCOneArgSelector: case DeclarationName::ObjCMultiArgSelector: N.getObjCSelector().print(OS); - return OS; + return; - case DeclarationName::CXXConstructorName: { - QualType ClassType = N.getCXXNameType(); - if (const RecordType *ClassRec = ClassType->getAs()) - return OS << *ClassRec->getDecl(); - LangOptions LO; - LO.CPlusPlus = true; - return OS << ClassType.getAsString(PrintingPolicy(LO)); - } + case DeclarationName::CXXConstructorName: + return printCXXConstructorDestructorName(N.getCXXNameType(), OS, Policy); case DeclarationName::CXXDestructorName: { OS << '~'; - QualType Type = N.getCXXNameType(); - if (const RecordType *Rec = Type->getAs()) - return OS << *Rec->getDecl(); - LangOptions LO; - LO.CPlusPlus = true; - return OS << Type.getAsString(PrintingPolicy(LO)); + return printCXXConstructorDestructorName(N.getCXXNameType(), OS, Policy); } case DeclarationName::CXXOperatorName: { @@ -178,29 +185,46 @@ raw_ostream &operator<<(raw_ostream &OS, DeclarationName N) { OS << "operator"; if (OpName[0] >= 'a' && OpName[0] <= 'z') OS << ' '; - return OS << OpName; + OS << OpName; + return; } case DeclarationName::CXXLiteralOperatorName: - return OS << "operator\"\"" << N.getCXXLiteralIdentifier()->getName(); + OS << "operator\"\"" << N.getCXXLiteralIdentifier()->getName(); + return; case DeclarationName::CXXConversionFunctionName: { OS << "operator "; QualType Type = N.getCXXNameType(); - if (const RecordType *Rec = Type->getAs()) - return OS << *Rec->getDecl(); - LangOptions LO; - LO.CPlusPlus = true; - LO.Bool = true; - return OS << Type.getAsString(PrintingPolicy(LO)); + if (const RecordType *Rec = Type->getAs()) { + OS << *Rec->getDecl(); + return; + } + if (!Policy.LangOpts.CPlusPlus) { + // Passed policy is the default one from operator <<, use a C++ policy. + LangOptions LO; + LO.CPlusPlus = true; + LO.Bool = true; + Type.print(OS, PrintingPolicy(LO)); + } else { + Type.print(OS, Policy); + } + return; } case DeclarationName::CXXUsingDirective: - return OS << ""; + OS << ""; + return; } llvm_unreachable("Unexpected declaration name kind"); } +raw_ostream &operator<<(raw_ostream &OS, DeclarationName N) { + LangOptions LO; + N.print(OS, PrintingPolicy(LO)); + return OS; +} + } // end namespace clang DeclarationName::NameKind DeclarationName::getNameKind() const { From d9adcc0af494be02de8ef0bde2fe69a677e98989 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Sun, 14 Feb 2016 06:39:03 +0000 Subject: [PATCH 148/742] [index] Allow calling createIndexingAction() without passing another action to wrap over. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260841 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Index/IndexingAction.h | 7 +-- lib/Index/IndexingAction.cpp | 78 ++++++++++++++++++++-------- tools/libclang/Indexing.cpp | 5 +- 3 files changed, 63 insertions(+), 27 deletions(-) diff --git a/include/clang/Index/IndexingAction.h b/include/clang/Index/IndexingAction.h index dfc363a049d..3bb427e36d8 100644 --- a/include/clang/Index/IndexingAction.h +++ b/include/clang/Index/IndexingAction.h @@ -32,10 +32,11 @@ struct IndexingOptions { bool IndexFunctionLocals = false; }; +/// \param WrappedAction another frontend action to wrap over or null. std::unique_ptr -createIndexingAction(std::unique_ptr WrappedAction, - std::shared_ptr DataConsumer, - IndexingOptions Opts); +createIndexingAction(std::shared_ptr DataConsumer, + IndexingOptions Opts, + std::unique_ptr WrappedAction = nullptr); void indexASTUnit(ASTUnit &Unit, std::shared_ptr DataConsumer, diff --git a/lib/Index/IndexingAction.cpp b/lib/Index/IndexingAction.cpp index 3f7ef43e7dc..46c96a35b6f 100644 --- a/lib/Index/IndexingAction.cpp +++ b/lib/Index/IndexingAction.cpp @@ -68,18 +68,52 @@ class IndexASTConsumer : public ASTConsumer { } }; -class IndexAction : public WrapperFrontendAction { - IndexingOptions IndexOpts; +class IndexActionBase { +protected: std::shared_ptr DataConsumer; - std::unique_ptr IndexCtx; + IndexingContext IndexCtx; + + IndexActionBase(std::shared_ptr dataConsumer, + IndexingOptions Opts) + : DataConsumer(std::move(dataConsumer)), + IndexCtx(Opts, *DataConsumer) {} + + std::unique_ptr createIndexASTConsumer() { + return llvm::make_unique(IndexCtx); + } + void finish() { + DataConsumer->finish(); + } +}; + +class IndexAction : public ASTFrontendAction, IndexActionBase { public: - IndexAction(std::unique_ptr WrappedAction, - std::shared_ptr DataConsumer, + IndexAction(std::shared_ptr DataConsumer, IndexingOptions Opts) + : IndexActionBase(std::move(DataConsumer), Opts) {} + +protected: + std::unique_ptr CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override { + return createIndexASTConsumer(); + } + + void EndSourceFileAction() override { + FrontendAction::EndSourceFileAction(); + finish(); + } +}; + +class WrappingIndexAction : public WrapperFrontendAction, IndexActionBase { + bool IndexActionFailed = false; + +public: + WrappingIndexAction(std::unique_ptr WrappedAction, + std::shared_ptr DataConsumer, + IndexingOptions Opts) : WrapperFrontendAction(std::move(WrappedAction)), - IndexOpts(Opts), - DataConsumer(std::move(DataConsumer)) {} + IndexActionBase(std::move(DataConsumer), Opts) {} protected: std::unique_ptr CreateASTConsumer(CompilerInstance &CI, @@ -89,36 +123,36 @@ class IndexAction : public WrapperFrontendAction { } // anonymous namespace -void IndexAction::EndSourceFileAction() { +void WrappingIndexAction::EndSourceFileAction() { // Invoke wrapped action's method. WrapperFrontendAction::EndSourceFileAction(); - - bool IndexActionFailed = !IndexCtx; if (!IndexActionFailed) - DataConsumer->finish(); + finish(); } std::unique_ptr -IndexAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { +WrappingIndexAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { auto OtherConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile); - if (!OtherConsumer) + if (!OtherConsumer) { + IndexActionFailed = true; return nullptr; - - IndexCtx.reset(new IndexingContext(IndexOpts, *DataConsumer)); + } std::vector> Consumers; Consumers.push_back(std::move(OtherConsumer)); - Consumers.push_back(llvm::make_unique(*IndexCtx)); + Consumers.push_back(createIndexASTConsumer()); return llvm::make_unique(std::move(Consumers)); } std::unique_ptr -index::createIndexingAction(std::unique_ptr WrappedAction, - std::shared_ptr DataConsumer, - IndexingOptions Opts) { - return llvm::make_unique(std::move(WrappedAction), - std::move(DataConsumer), - Opts); +index::createIndexingAction(std::shared_ptr DataConsumer, + IndexingOptions Opts, + std::unique_ptr WrappedAction) { + if (WrappedAction) + return llvm::make_unique(std::move(WrappedAction), + std::move(DataConsumer), + Opts); + return llvm::make_unique(std::move(DataConsumer), Opts); } diff --git a/tools/libclang/Indexing.cpp b/tools/libclang/Indexing.cpp index b37a8864dd6..7b2bcc7aac2 100644 --- a/tools/libclang/Indexing.cpp +++ b/tools/libclang/Indexing.cpp @@ -564,8 +564,9 @@ static CXErrorCode clang_indexSourceFile_Impl( auto InterAction = llvm::make_unique(DataConsumer, SkipBodies ? IdxSession->SkipBodyData.get() : nullptr); std::unique_ptr IndexAction; - IndexAction = createIndexingAction(std::move(InterAction), DataConsumer, - getIndexingOptionsFromCXOptions(index_options)); + IndexAction = createIndexingAction(DataConsumer, + getIndexingOptionsFromCXOptions(index_options), + std::move(InterAction)); // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar From f2708380b560cf98226106468ba012967411cbc8 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Sun, 14 Feb 2016 06:39:11 +0000 Subject: [PATCH 149/742] [index] Enhance c-index-test tool and have it link and test the clangIndex library directly. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260842 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Index/IndexSymbol.h | 8 ++ lib/Index/IndexSymbol.cpp | 99 +++++++++++++++ test/Index/Core/index-source.m | 8 ++ tools/c-index-test/CMakeLists.txt | 7 ++ tools/c-index-test/c-index-test.c | 20 ++- tools/c-index-test/core_main.cpp | 197 ++++++++++++++++++++++++++++++ 6 files changed, 335 insertions(+), 4 deletions(-) create mode 100644 test/Index/Core/index-source.m create mode 100644 tools/c-index-test/core_main.cpp diff --git a/include/clang/Index/IndexSymbol.h b/include/clang/Index/IndexSymbol.h index 7a505eac415..feee13cdbf5 100644 --- a/include/clang/Index/IndexSymbol.h +++ b/include/clang/Index/IndexSymbol.h @@ -11,6 +11,7 @@ #define LLVM_CLANG_INDEX_INDEXSYMBOL_H #include "clang/Basic/LLVM.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/Support/DataTypes.h" namespace clang { @@ -107,6 +108,13 @@ struct SymbolInfo { SymbolInfo getSymbolInfo(const Decl *D); +void applyForEachSymbolRole(SymbolRoleSet Roles, + llvm::function_ref Fn); +void printSymbolRoles(SymbolRoleSet Roles, raw_ostream &OS); +StringRef getSymbolKindString(SymbolKind K); +StringRef getTemplateKindStr(SymbolCXXTemplateKind TK); +StringRef getSymbolLanguageString(SymbolLanguage K); + } // namespace index } // namespace clang diff --git a/lib/Index/IndexSymbol.cpp b/lib/Index/IndexSymbol.cpp index c7c3c8c4d64..bf3bfd1b95b 100644 --- a/lib/Index/IndexSymbol.cpp +++ b/lib/Index/IndexSymbol.cpp @@ -185,3 +185,102 @@ SymbolInfo index::getSymbolInfo(const Decl *D) { return Info; } + +void index::applyForEachSymbolRole(SymbolRoleSet Roles, + llvm::function_ref Fn) { +#define APPLY_FOR_ROLE(Role) \ + if (Roles & (unsigned)SymbolRole::Role) \ + Fn(SymbolRole::Role) + + APPLY_FOR_ROLE(Declaration); + APPLY_FOR_ROLE(Definition); + APPLY_FOR_ROLE(Reference); + APPLY_FOR_ROLE(Read); + APPLY_FOR_ROLE(Write); + APPLY_FOR_ROLE(Call); + APPLY_FOR_ROLE(Dynamic); + APPLY_FOR_ROLE(AddressOf); + APPLY_FOR_ROLE(Implicit); + APPLY_FOR_ROLE(RelationChildOf); + APPLY_FOR_ROLE(RelationBaseOf); + APPLY_FOR_ROLE(RelationOverrideOf); + APPLY_FOR_ROLE(RelationReceivedBy); + +#undef APPLY_FOR_ROLE +} + +void index::printSymbolRoles(SymbolRoleSet Roles, raw_ostream &OS) { + bool VisitedOnce = false; + applyForEachSymbolRole(Roles, [&](SymbolRole Role) { + if (VisitedOnce) + OS << '/'; + else + VisitedOnce = true; + switch (Role) { + case SymbolRole::Declaration: OS << "Decl"; break; + case SymbolRole::Definition: OS << "Def"; break; + case SymbolRole::Reference: OS << "Ref"; break; + case SymbolRole::Read: OS << "Read"; break; + case SymbolRole::Write: OS << "Writ"; break; + case SymbolRole::Call: OS << "Call"; break; + case SymbolRole::Dynamic: OS << "Dyn"; break; + case SymbolRole::AddressOf: OS << "Addr"; break; + case SymbolRole::Implicit: OS << "Impl"; break; + case SymbolRole::RelationChildOf: OS << "RelChild"; break; + case SymbolRole::RelationBaseOf: OS << "RelBase"; break; + case SymbolRole::RelationOverrideOf: OS << "RelOver"; break; + case SymbolRole::RelationReceivedBy: OS << "RelRec"; break; + } + }); +} + +StringRef index::getSymbolKindString(SymbolKind K) { + switch (K) { + case SymbolKind::Unknown: return ""; + case SymbolKind::Module: return "module"; + case SymbolKind::Macro: return "macro"; + case SymbolKind::Enum: return "enum"; + case SymbolKind::Struct: return "struct"; + case SymbolKind::Union: return "union"; + case SymbolKind::Typedef: return "typedef"; + case SymbolKind::Function: return "function"; + case SymbolKind::Variable: return "variable"; + case SymbolKind::Field: return "field"; + case SymbolKind::EnumConstant: return "enumerator"; + case SymbolKind::ObjCClass: return "objc-class"; + case SymbolKind::ObjCProtocol: return "objc-protocol"; + case SymbolKind::ObjCCategory: return "objc-category"; + case SymbolKind::ObjCInstanceMethod: return "objc-instance-method"; + case SymbolKind::ObjCClassMethod: return "objc-class-method"; + case SymbolKind::ObjCProperty: return "objc-property"; + case SymbolKind::ObjCIvar: return "objc-ivar"; + case SymbolKind::CXXClass: return "c++-class"; + case SymbolKind::CXXNamespace: return "namespace"; + case SymbolKind::CXXNamespaceAlias: return "namespace-alias"; + case SymbolKind::CXXStaticVariable: return "c++-static-var"; + case SymbolKind::CXXStaticMethod: return "c++-static-method"; + case SymbolKind::CXXInstanceMethod: return "c++-instance-method"; + case SymbolKind::CXXConstructor: return "constructor"; + case SymbolKind::CXXDestructor: return "destructor"; + case SymbolKind::CXXConversionFunction: return "coversion-func"; + case SymbolKind::CXXTypeAlias: return "type-alias"; + case SymbolKind::CXXInterface: return "c++-__interface"; + } +} + +StringRef index::getTemplateKindStr(SymbolCXXTemplateKind TK) { + switch (TK) { + case SymbolCXXTemplateKind::NonTemplate: return "NT"; + case SymbolCXXTemplateKind::Template : return "T"; + case SymbolCXXTemplateKind::TemplatePartialSpecialization : return "TPS"; + case SymbolCXXTemplateKind::TemplateSpecialization: return "TS"; + } +} + +StringRef index::getSymbolLanguageString(SymbolLanguage K) { + switch (K) { + case SymbolLanguage::C: return "C"; + case SymbolLanguage::ObjC: return "ObjC"; + case SymbolLanguage::CXX: return "C++"; + } +} diff --git a/test/Index/Core/index-source.m b/test/Index/Core/index-source.m new file mode 100644 index 00000000000..42c23505b1b --- /dev/null +++ b/test/Index/Core/index-source.m @@ -0,0 +1,8 @@ +// RUN: c-index-test core -print-source-symbols -- %s | FileCheck %s + +@interface Base +// CHECK: [[@LINE-1]]:12 | objc-class/ObjC | Base | c:objc(cs)Base | Decl | rel: 0 +-(void)meth; +// CHECK: [[@LINE-1]]:1 | objc-instance-method/ObjC | meth | c:objc(cs)Base(im)meth | Decl/Dyn/RelChild | rel: 1 +// CHECK-NEXT: RelChild | Base | c:objc(cs)Base +@end diff --git a/tools/c-index-test/CMakeLists.txt b/tools/c-index-test/CMakeLists.txt index c78a42ffe8e..1228a654864 100644 --- a/tools/c-index-test/CMakeLists.txt +++ b/tools/c-index-test/CMakeLists.txt @@ -1,5 +1,10 @@ +set(LLVM_LINK_COMPONENTS + support +) + add_clang_executable(c-index-test c-index-test.c + core_main.cpp ) if(NOT MSVC) @@ -12,10 +17,12 @@ endif() if (LLVM_BUILD_STATIC) target_link_libraries(c-index-test libclang_static + clangIndex ) else() target_link_libraries(c-index-test libclang + clangIndex ) endif() diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c index b2f9120baf9..a67afb7cfa8 100644 --- a/tools/c-index-test/c-index-test.c +++ b/tools/c-index-test/c-index-test.c @@ -23,6 +23,8 @@ # include #endif +extern int indextest_core_main(int argc, const char **argv); + /******************************************************************************/ /* Utility functions. */ /******************************************************************************/ @@ -4410,13 +4412,15 @@ int cindextest_main(int argc, const char **argv) { * size). */ typedef struct thread_info { + int (*main_func)(int argc, const char **argv); int argc; const char **argv; int result; } thread_info; void thread_runner(void *client_data_v) { thread_info *client_data = client_data_v; - client_data->result = cindextest_main(client_data->argc, client_data->argv); + client_data->result = client_data->main_func(client_data->argc, + client_data->argv); } static void flush_atexit(void) { @@ -4435,11 +4439,19 @@ int main(int argc, const char **argv) { LIBXML_TEST_VERSION #endif - if (getenv("CINDEXTEST_NOTHREADS")) - return cindextest_main(argc, argv); - + client_data.main_func = cindextest_main; client_data.argc = argc; client_data.argv = argv; + + if (argc > 1 && strcmp(argv[1], "core") == 0) { + client_data.main_func = indextest_core_main; + --client_data.argc; + ++client_data.argv; + } + + if (getenv("CINDEXTEST_NOTHREADS")) + return client_data.main_func(client_data.argc, client_data.argv); + clang_executeOnThread(thread_runner, &client_data, 0); return client_data.result; } diff --git a/tools/c-index-test/core_main.cpp b/tools/c-index-test/core_main.cpp new file mode 100644 index 00000000000..d2faf2d0692 --- /dev/null +++ b/tools/c-index-test/core_main.cpp @@ -0,0 +1,197 @@ +//===-- core_main.cpp - Core Index Tool testbed ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Index/IndexingAction.h" +#include "clang/Index/IndexDataConsumer.h" +#include "clang/Index/USRGeneration.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/PrettyStackTrace.h" + +using namespace clang; +using namespace clang::index; +using namespace llvm; + +extern "C" int indextest_core_main(int argc, const char **argv); + +namespace { + +enum class ActionType { + None, + PrintSourceSymbols, +}; + +namespace options { + +static cl::OptionCategory IndexTestCoreCategory("index-test-core options"); + +static cl::opt +Action(cl::desc("Action:"), cl::init(ActionType::None), + cl::values( + clEnumValN(ActionType::PrintSourceSymbols, + "print-source-symbols", "Print symbols from source"), + clEnumValEnd), + cl::cat(IndexTestCoreCategory)); + +static cl::extrahelp MoreHelp( + "\nAdd \"-- \" at the end to setup the compiler " + "invocation\n" +); + +} +} // anonymous namespace + +static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS); +static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx, + raw_ostream &OS); + +namespace { + +class PrintIndexDataConsumer : public IndexDataConsumer { + raw_ostream &OS; + +public: + PrintIndexDataConsumer(raw_ostream &OS) : OS(OS) { + } + + bool handleDeclOccurence(const Decl *D, SymbolRoleSet Roles, + ArrayRef Relations, + FileID FID, unsigned Offset, + ASTNodeInfo ASTNode) override { + ASTContext &Ctx = D->getASTContext(); + SourceManager &SM = Ctx.getSourceManager(); + + unsigned Line = SM.getLineNumber(FID, Offset); + unsigned Col = SM.getColumnNumber(FID, Offset); + OS << Line << ':' << Col << " | "; + + printSymbolInfo(getSymbolInfo(D), OS); + OS << " | "; + + printSymbolNameAndUSR(D, Ctx, OS); + OS << " | "; + + printSymbolRoles(Roles, OS); + OS << " | "; + + OS << "rel: " << Relations.size() << '\n'; + + for (auto &SymRel : Relations) { + OS << '\t'; + printSymbolRoles(SymRel.Roles, OS); + OS << " | "; + printSymbolNameAndUSR(SymRel.RelatedSymbol, Ctx, OS); + OS << '\n'; + } + + return true; + } +}; + +} // anonymous namespace + +//===----------------------------------------------------------------------===// +// Print Source Symbols +//===----------------------------------------------------------------------===// + +static bool printSourceSymbols(ArrayRef Args) { + SmallVector ArgsWithProgName; + ArgsWithProgName.push_back("clang"); + ArgsWithProgName.append(Args.begin(), Args.end()); + IntrusiveRefCntPtr + Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions)); + IntrusiveRefCntPtr + CInvok(createInvocationFromCommandLine(ArgsWithProgName, Diags)); + if (!CInvok) + return true; + + auto DataConsumer = std::make_shared(outs()); + IndexingOptions IndexOpts; + std::unique_ptr IndexAction; + IndexAction = createIndexingAction(DataConsumer, IndexOpts); + + auto PCHContainerOps = std::make_shared(); + ASTUnit *Unit = + ASTUnit::LoadFromCompilerInvocationAction(CInvok.get(), PCHContainerOps, + Diags, IndexAction.get()); + + if (!Unit) + return true; + + return false; +} + +//===----------------------------------------------------------------------===// +// Helper Utils +//===----------------------------------------------------------------------===// + +static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS) { + OS << getSymbolKindString(SymInfo.Kind); + if (SymInfo.TemplateKind != SymbolCXXTemplateKind::NonTemplate) { + OS << '-' << getTemplateKindStr(SymInfo.TemplateKind); + } + OS << '/' << getSymbolLanguageString(SymInfo.Lang); +} + +static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx, + raw_ostream &OS) { + if (auto *ND = dyn_cast(D)) { + PrintingPolicy PrintPolicy(Ctx.getLangOpts()); + ND->getDeclName().print(OS, PrintPolicy); + } else { + OS << ""; + } + OS << " | "; + + SmallString<256> USRBuf; + if (generateUSRForDecl(D, USRBuf)) { + OS << ""; + } else { + OS << USRBuf; + } +} + +//===----------------------------------------------------------------------===// +// Command line processing. +//===----------------------------------------------------------------------===// + +int indextest_core_main(int argc, const char **argv) { + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + + std::vector CompArgs; + const char *const *DoubleDash = std::find(argv, argv + argc, StringRef("--")); + if (DoubleDash != argv + argc) { + CompArgs = std::vector(DoubleDash + 1, argv + argc); + argc = DoubleDash - argv; + } + + cl::HideUnrelatedOptions(options::IndexTestCoreCategory); + cl::ParseCommandLineOptions(argc, argv, "index-test-core"); + + if (options::Action == ActionType::None) { + errs() << "error: action required; pass '-help' for options\n"; + return 1; + } + + if (options::Action == ActionType::PrintSourceSymbols) { + if (CompArgs.empty()) { + errs() << "error: missing compiler args; pass '-- '\n"; + return 1; + } + return printSourceSymbols(CompArgs); + } + + return 0; +} From 5cabeb307e74e141a1539de73283229619a08a2a Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Sun, 14 Feb 2016 06:53:20 +0000 Subject: [PATCH 150/742] [index] Fix gcc builds. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260843 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Index/IndexingAction.h | 2 +- tools/c-index-test/core_main.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/clang/Index/IndexingAction.h b/include/clang/Index/IndexingAction.h index 3bb427e36d8..e2e63dc6357 100644 --- a/include/clang/Index/IndexingAction.h +++ b/include/clang/Index/IndexingAction.h @@ -36,7 +36,7 @@ struct IndexingOptions { std::unique_ptr createIndexingAction(std::shared_ptr DataConsumer, IndexingOptions Opts, - std::unique_ptr WrappedAction = nullptr); + std::unique_ptr WrappedAction); void indexASTUnit(ASTUnit &Unit, std::shared_ptr DataConsumer, diff --git a/tools/c-index-test/core_main.cpp b/tools/c-index-test/core_main.cpp index d2faf2d0692..00fbd0c2dad 100644 --- a/tools/c-index-test/core_main.cpp +++ b/tools/c-index-test/core_main.cpp @@ -119,7 +119,8 @@ static bool printSourceSymbols(ArrayRef Args) { auto DataConsumer = std::make_shared(outs()); IndexingOptions IndexOpts; std::unique_ptr IndexAction; - IndexAction = createIndexingAction(DataConsumer, IndexOpts); + IndexAction = createIndexingAction(DataConsumer, IndexOpts, + /*WrappedAction=*/nullptr); auto PCHContainerOps = std::make_shared(); ASTUnit *Unit = From 53d17543fcb146ce4886779a8166bd9479e1af59 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Sun, 14 Feb 2016 07:08:31 +0000 Subject: [PATCH 151/742] [c-index-test] Fix a gcc build error. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260844 91177308-0d34-0410-b5e6-96231b3b80d8 --- tools/c-index-test/core_main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/c-index-test/core_main.cpp b/tools/c-index-test/core_main.cpp index 00fbd0c2dad..ce37b562561 100644 --- a/tools/c-index-test/core_main.cpp +++ b/tools/c-index-test/core_main.cpp @@ -172,7 +172,7 @@ int indextest_core_main(int argc, const char **argv) { PrettyStackTraceProgram X(argc, argv); std::vector CompArgs; - const char *const *DoubleDash = std::find(argv, argv + argc, StringRef("--")); + const char **DoubleDash = std::find(argv, argv + argc, StringRef("--")); if (DoubleDash != argv + argc) { CompArgs = std::vector(DoubleDash + 1, argv + argc); argc = DoubleDash - argv; From 511bbcffaaa73ed9642a4cc72afe53cf41a9711c Mon Sep 17 00:00:00 2001 From: NAKAMURA Takumi Date: Sun, 14 Feb 2016 09:19:04 +0000 Subject: [PATCH 152/742] c-index-test: Fix libdeps corresponding to r260841. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260847 91177308-0d34-0410-b5e6-96231b3b80d8 --- tools/c-index-test/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/c-index-test/CMakeLists.txt b/tools/c-index-test/CMakeLists.txt index 1228a654864..3958e95a90f 100644 --- a/tools/c-index-test/CMakeLists.txt +++ b/tools/c-index-test/CMakeLists.txt @@ -22,6 +22,9 @@ if (LLVM_BUILD_STATIC) else() target_link_libraries(c-index-test libclang + clangAST + clangBasic + clangFrontend clangIndex ) endif() From 75ab7dca4d54577ecb619f67ffebf6ad20761a4d Mon Sep 17 00:00:00 2001 From: Benjamin Kramer Date: Sun, 14 Feb 2016 13:18:06 +0000 Subject: [PATCH 153/742] Don't leak the ASTUnit when done with testing. Found by lsan. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260850 91177308-0d34-0410-b5e6-96231b3b80d8 --- tools/c-index-test/core_main.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/c-index-test/core_main.cpp b/tools/c-index-test/core_main.cpp index ce37b562561..c56d66f2147 100644 --- a/tools/c-index-test/core_main.cpp +++ b/tools/c-index-test/core_main.cpp @@ -123,9 +123,8 @@ static bool printSourceSymbols(ArrayRef Args) { /*WrappedAction=*/nullptr); auto PCHContainerOps = std::make_shared(); - ASTUnit *Unit = - ASTUnit::LoadFromCompilerInvocationAction(CInvok.get(), PCHContainerOps, - Diags, IndexAction.get()); + std::unique_ptr Unit(ASTUnit::LoadFromCompilerInvocationAction( + CInvok.get(), PCHContainerOps, Diags, IndexAction.get())); if (!Unit) return true; From cc4c80622b7af2b29a4048bba071ae8bcfd1ba37 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Sat, 6 Feb 2016 22:36:34 +0000 Subject: [PATCH 154/742] Index: provide adjustment thunk information for C++ manglings Add support for exposing the adjustment thunk for virtual methods as appropriate. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260011 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/Index/print-cxx-manglings.cpp | 30 +++++++++++++++++++++++ tools/libclang/CIndex.cpp | 39 ++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/test/Index/print-cxx-manglings.cpp b/test/Index/print-cxx-manglings.cpp index aae29930172..27d7988eb79 100644 --- a/test/Index/print-cxx-manglings.cpp +++ b/test/Index/print-cxx-manglings.cpp @@ -64,3 +64,33 @@ struct v { // MSVC: CXXConstructor=v{{.*}}[mangled=??0v@@QAE@H@Z] [mangled=??_Fv@@QAEXXZ] +struct w { + virtual int m(int); +}; + +// ITANIUM: CXXMethod=m{{.*}} (virtual) [mangled=_ZN1w1mEi] + +// MACHO: CXXMethod=m{{.*}} (virtual) [mangled=__ZN1w1mEi] + +// MSVC: CXXMethod=m{{.*}} (virtual) [mangled=?m@w@@UAEHH@Z] + +struct x { + virtual int m(int); +}; + +// ITANIUM: CXXMethod=m{{.*}} (virtual) [mangled=_ZN1x1mEi] + +// MACHO: CXXMethod=m{{.*}} (virtual) [mangled=__ZN1x1mEi] + +// MSVC: CXXMethod=m{{.*}} (virtual) [mangled=?m@x@@UAEHH@Z] + +struct y : w, x { + virtual int m(int); +}; + +// ITANIUM: CXXMethod=m{{.*}} (virtual) {{.*}} [mangled=_ZN1y1mEi] [mangled=_ZThn4_N1y1mEi] + +// MACHO: CXXMethod=m{{.*}} (virtual) {{.*}} [mangled=__ZN1y1mEi] [mangled=__ZThn4_N1y1mEi] + +// MSVC: CXXMethod=m{{.*}} (virtual) {{.*}} [mangled=?m@y@@UAEHH@Z] [mangled=?m@y@@W3AEHH@Z] + diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index 9963f328ff6..448784e054f 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -24,6 +24,7 @@ #include "clang/AST/Attr.h" #include "clang/AST/Mangle.h" #include "clang/AST/StmtVisitor.h" +#include "clang/AST/VTableBuilder.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/DiagnosticCategories.h" #include "clang/Basic/DiagnosticIDs.h" @@ -4341,6 +4342,38 @@ CXString clang_Cursor_getMangling(CXCursor C) { return cxstring::createDup(FinalBufOS.str()); } +static std::string getMangledName(std::unique_ptr &M, + std::unique_ptr &DL, + const NamedDecl *ND) { + std::string FrontendBuf; + llvm::raw_string_ostream FOS(FrontendBuf); + + M->mangleName(ND, FOS); + + std::string BackendBuf; + llvm::raw_string_ostream BOS(BackendBuf); + + llvm::Mangler::getNameWithPrefix(BOS, llvm::Twine(FOS.str()), *DL); + + return BOS.str(); +} + +static std::string getMangledThunk(std::unique_ptr &M, + std::unique_ptr &DL, + const CXXMethodDecl *MD, const ThunkInfo &T) { + std::string FrontendBuf; + llvm::raw_string_ostream FOS(FrontendBuf); + + M->mangleThunk(MD, T, FOS); + + std::string BackendBuf; + llvm::raw_string_ostream BOS(BackendBuf); + + llvm::Mangler::getNameWithPrefix(BOS, llvm::Twine(FOS.str()), *DL); + + return BOS.str(); +} + CXStringSet *clang_Cursor_getCXXManglings(CXCursor C) { if (clang_isInvalid(C.kind) || !clang_isDeclaration(C.kind)) return nullptr; @@ -4384,6 +4417,12 @@ CXStringSet *clang_Cursor_getCXXManglings(CXCursor C) { if (DD->isVirtual()) Manglings.emplace_back(getMangledStructor(M, DL, DD, Dtor_Deleting)); } + } else if (const auto *MD = dyn_cast_or_null(ND)) { + Manglings.emplace_back(getMangledName(M, DL, ND)); + if (MD->isVirtual()) + if (const auto *TIV = Ctx.getVTableContext()->getThunkInfo(MD)) + for (const auto &T : *TIV) + Manglings.emplace_back(getMangledThunk(M, DL, MD, T)); } return cxstring::createSet(Manglings); From 1766eab65ab403ff42949cbbf15b5e42344ac5d8 Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Mon, 8 Feb 2016 15:52:13 +0000 Subject: [PATCH 155/742] Move static functions returning UDTs outside of the extern "C" block. Silences an MSVC warning, and reduces the number of exported symbols. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260104 91177308-0d34-0410-b5e6-96231b3b80d8 --- tools/libclang/CIndex.cpp | 65 ++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index 448784e054f..ad162f0366c 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -3969,6 +3969,39 @@ static std::string getMangledStructor(std::unique_ptr &M, return BOS.str(); } +static std::string getMangledName(std::unique_ptr &M, + std::unique_ptr &DL, + const NamedDecl *ND) { + std::string FrontendBuf; + llvm::raw_string_ostream FOS(FrontendBuf); + + M->mangleName(ND, FOS); + + std::string BackendBuf; + llvm::raw_string_ostream BOS(BackendBuf); + + llvm::Mangler::getNameWithPrefix(BOS, llvm::Twine(FOS.str()), *DL); + + return BOS.str(); +} + +static std::string getMangledThunk(std::unique_ptr &M, + std::unique_ptr &DL, + const CXXMethodDecl *MD, + const ThunkInfo &T) { + std::string FrontendBuf; + llvm::raw_string_ostream FOS(FrontendBuf); + + M->mangleThunk(MD, T, FOS); + + std::string BackendBuf; + llvm::raw_string_ostream BOS(BackendBuf); + + llvm::Mangler::getNameWithPrefix(BOS, llvm::Twine(FOS.str()), *DL); + + return BOS.str(); +} + extern "C" { unsigned clang_visitChildren(CXCursor parent, @@ -4342,38 +4375,6 @@ CXString clang_Cursor_getMangling(CXCursor C) { return cxstring::createDup(FinalBufOS.str()); } -static std::string getMangledName(std::unique_ptr &M, - std::unique_ptr &DL, - const NamedDecl *ND) { - std::string FrontendBuf; - llvm::raw_string_ostream FOS(FrontendBuf); - - M->mangleName(ND, FOS); - - std::string BackendBuf; - llvm::raw_string_ostream BOS(BackendBuf); - - llvm::Mangler::getNameWithPrefix(BOS, llvm::Twine(FOS.str()), *DL); - - return BOS.str(); -} - -static std::string getMangledThunk(std::unique_ptr &M, - std::unique_ptr &DL, - const CXXMethodDecl *MD, const ThunkInfo &T) { - std::string FrontendBuf; - llvm::raw_string_ostream FOS(FrontendBuf); - - M->mangleThunk(MD, T, FOS); - - std::string BackendBuf; - llvm::raw_string_ostream BOS(BackendBuf); - - llvm::Mangler::getNameWithPrefix(BOS, llvm::Twine(FOS.str()), *DL); - - return BOS.str(); -} - CXStringSet *clang_Cursor_getCXXManglings(CXCursor C) { if (clang_isInvalid(C.kind) || !clang_isDeclaration(C.kind)) return nullptr; From c3ac7169b4441c1c4d616b86a87863c92138ee68 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Sun, 14 Feb 2016 22:30:14 +0000 Subject: [PATCH 156/742] [index] Factor libclang's functionality to determing the mangled name of symbols into the clangIndex library. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260858 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Mangle.h | 1 + include/clang/Index/CodegenNameGenerator.h | 52 ++++++ include/clang/Index/IndexDataConsumer.h | 3 + lib/AST/Mangle.cpp | 16 +- lib/Index/CMakeLists.txt | 3 +- lib/Index/CodegenNameGenerator.cpp | 197 +++++++++++++++++++++ lib/Index/IndexingAction.cpp | 2 + test/Index/Core/index-source.m | 4 +- tools/c-index-test/core_main.cpp | 10 ++ tools/libclang/CIndex.cpp | 131 +------------- 10 files changed, 286 insertions(+), 133 deletions(-) create mode 100644 include/clang/Index/CodegenNameGenerator.h create mode 100644 lib/Index/CodegenNameGenerator.cpp diff --git a/include/clang/AST/Mangle.h b/include/clang/AST/Mangle.h index 4872738e7a1..dad9269f842 100644 --- a/include/clang/AST/Mangle.h +++ b/include/clang/AST/Mangle.h @@ -123,6 +123,7 @@ class MangleContext { void mangleBlock(const DeclContext *DC, const BlockDecl *BD, raw_ostream &Out); + void mangleObjCMethodNameWithoutSize(const ObjCMethodDecl *MD, raw_ostream &); void mangleObjCMethodName(const ObjCMethodDecl *MD, raw_ostream &); virtual void mangleStaticGuardVariable(const VarDecl *D, raw_ostream &) = 0; diff --git a/include/clang/Index/CodegenNameGenerator.h b/include/clang/Index/CodegenNameGenerator.h new file mode 100644 index 00000000000..e8dc196a204 --- /dev/null +++ b/include/clang/Index/CodegenNameGenerator.h @@ -0,0 +1,52 @@ +//===- CodegenNameGenerator.h - Codegen name generation -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Determines the name that the symbol will get for code generation. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_CODEGENNAMEGENERATOR_H +#define LLVM_CLANG_INDEX_CODEGENNAMEGENERATOR_H + +#include "clang/Basic/LLVM.h" +#include +#include +#include + +namespace clang { + class ASTContext; + class Decl; + +namespace index { + +class CodegenNameGenerator { +public: + explicit CodegenNameGenerator(ASTContext &Ctx); + ~CodegenNameGenerator(); + + /// \returns true on failure to produce a name for the given decl, false on + /// success. + bool writeName(const Decl *D, raw_ostream &OS); + + /// Version of \c writeName function that returns a string. + std::string getName(const Decl *D); + + /// This can return multiple mangled names when applicable, e.g. for C++ + /// constructors/destructors. + std::vector getAllManglings(const Decl *D); + +private: + struct Implementation; + std::unique_ptr Impl; +}; + +} // namespace index +} // namespace clang + +#endif // LLVM_CLANG_INDEX_CODEGENNAMEGENERATOR_H diff --git a/include/clang/Index/IndexDataConsumer.h b/include/clang/Index/IndexDataConsumer.h index cb00345f56b..31543704834 100644 --- a/include/clang/Index/IndexDataConsumer.h +++ b/include/clang/Index/IndexDataConsumer.h @@ -13,6 +13,7 @@ #include "clang/Index/IndexSymbol.h" namespace clang { + class ASTContext; class DeclContext; class Expr; class FileID; @@ -33,6 +34,8 @@ class IndexDataConsumer { virtual ~IndexDataConsumer() {} + virtual void initialize(ASTContext &Ctx) {} + /// \returns true to continue indexing, or false to abort. virtual bool handleDeclOccurence(const Decl *D, SymbolRoleSet Roles, ArrayRef Relations, diff --git a/lib/AST/Mangle.cpp b/lib/AST/Mangle.cpp index b1193ca2d23..7a9cf18acc1 100644 --- a/lib/AST/Mangle.cpp +++ b/lib/AST/Mangle.cpp @@ -254,11 +254,8 @@ void MangleContext::mangleBlock(const DeclContext *DC, const BlockDecl *BD, mangleFunctionBlock(*this, Buffer, BD, Out); } -void MangleContext::mangleObjCMethodName(const ObjCMethodDecl *MD, - raw_ostream &Out) { - SmallString<64> Name; - llvm::raw_svector_ostream OS(Name); - +void MangleContext::mangleObjCMethodNameWithoutSize(const ObjCMethodDecl *MD, + raw_ostream &OS) { const ObjCContainerDecl *CD = dyn_cast(MD->getDeclContext()); assert (CD && "Missing container decl in GetNameForMethod"); @@ -268,6 +265,13 @@ void MangleContext::mangleObjCMethodName(const ObjCMethodDecl *MD, OS << ' '; MD->getSelector().print(OS); OS << ']'; - +} + +void MangleContext::mangleObjCMethodName(const ObjCMethodDecl *MD, + raw_ostream &Out) { + SmallString<64> Name; + llvm::raw_svector_ostream OS(Name); + + mangleObjCMethodNameWithoutSize(MD, OS); Out << OS.str().size() << OS.str(); } diff --git a/lib/Index/CMakeLists.txt b/lib/Index/CMakeLists.txt index cb2b931235e..7e39e498c8f 100644 --- a/lib/Index/CMakeLists.txt +++ b/lib/Index/CMakeLists.txt @@ -3,14 +3,15 @@ set(LLVM_LINK_COMPONENTS ) add_clang_library(clangIndex + CodegenNameGenerator.cpp CommentToXML.cpp - USRGeneration.cpp IndexBody.cpp IndexDecl.cpp IndexingAction.cpp IndexingContext.cpp IndexSymbol.cpp IndexTypeSourceInfo.cpp + USRGeneration.cpp ADDITIONAL_HEADERS IndexingContext.h diff --git a/lib/Index/CodegenNameGenerator.cpp b/lib/Index/CodegenNameGenerator.cpp new file mode 100644 index 00000000000..d663cc36b70 --- /dev/null +++ b/lib/Index/CodegenNameGenerator.cpp @@ -0,0 +1,197 @@ +//===- CodegenNameGenerator.cpp - Codegen name generation -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Determines the name that the symbol will get for code generation. +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/CodegenNameGenerator.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Mangle.h" +#include "clang/AST/VTableBuilder.h" +#include "clang/Basic/TargetInfo.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/Mangler.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::index; + +struct CodegenNameGenerator::Implementation { + std::unique_ptr MC; + llvm::DataLayout DL; + + Implementation(ASTContext &Ctx) + : MC(Ctx.createMangleContext()), + DL(Ctx.getTargetInfo().getDataLayoutString()) {} + + bool writeName(const Decl *D, raw_ostream &OS) { + // First apply frontend mangling. + SmallString<128> FrontendBuf; + llvm::raw_svector_ostream FrontendBufOS(FrontendBuf); + if (auto *FD = dyn_cast(D)) { + if (FD->isDependentContext()) + return true; + if (writeFuncOrVarName(FD, FrontendBufOS)) + return true; + } else if (auto *VD = dyn_cast(D)) { + if (writeFuncOrVarName(VD, FrontendBufOS)) + return true; + } else if (auto *MD = dyn_cast(D)) { + MC->mangleObjCMethodNameWithoutSize(MD, OS); + return false; + } else if (auto *ID = dyn_cast(D)) { + writeObjCClassName(ID, FrontendBufOS); + } else { + return true; + } + + // Now apply backend mangling. + llvm::Mangler::getNameWithPrefix(OS, FrontendBufOS.str(), DL); + return false; + } + + std::string getName(const Decl *D) { + std::string Name; + { + llvm::raw_string_ostream OS(Name); + writeName(D, OS); + } + return Name; + } + + std::vector getAllManglings(const Decl *D) { + if (!(isa(D) || isa(D))) + return {}; + + const NamedDecl *ND = cast(D); + + ASTContext &Ctx = ND->getASTContext(); + std::unique_ptr M(Ctx.createMangleContext()); + std::unique_ptr DL( + new llvm::DataLayout(Ctx.getTargetInfo().getDataLayoutString())); + + std::vector Manglings; + + auto hasDefaultCXXMethodCC = [](ASTContext &C, const CXXMethodDecl *MD) { + auto DefaultCC = C.getDefaultCallingConvention(/*IsVariadic=*/false, + /*IsCSSMethod=*/true); + auto CC = MD->getType()->getAs()->getCallConv(); + return CC == DefaultCC; + }; + + if (const auto *CD = dyn_cast_or_null(ND)) { + Manglings.emplace_back(getMangledStructor(CD, Ctor_Base)); + + if (Ctx.getTargetInfo().getCXXABI().isItaniumFamily()) + if (!CD->getParent()->isAbstract()) + Manglings.emplace_back(getMangledStructor(CD, Ctor_Complete)); + + if (Ctx.getTargetInfo().getCXXABI().isMicrosoft()) + if (CD->hasAttr() && CD->isDefaultConstructor()) + if (!(hasDefaultCXXMethodCC(Ctx, CD) && CD->getNumParams() == 0)) + Manglings.emplace_back(getMangledStructor(CD, Ctor_DefaultClosure)); + } else if (const auto *DD = dyn_cast_or_null(ND)) { + Manglings.emplace_back(getMangledStructor(DD, Dtor_Base)); + if (Ctx.getTargetInfo().getCXXABI().isItaniumFamily()) { + Manglings.emplace_back(getMangledStructor(DD, Dtor_Complete)); + if (DD->isVirtual()) + Manglings.emplace_back(getMangledStructor(DD, Dtor_Deleting)); + } + } else if (const auto *MD = dyn_cast_or_null(ND)) { + Manglings.emplace_back(getName(ND)); + if (MD->isVirtual()) + if (const auto *TIV = Ctx.getVTableContext()->getThunkInfo(MD)) + for (const auto &T : *TIV) + Manglings.emplace_back(getMangledThunk(MD, T)); + } + + return Manglings; + } + +private: + bool writeFuncOrVarName(const NamedDecl *D, raw_ostream &OS) { + if (MC->shouldMangleDeclName(D)) { + if (const auto *CtorD = dyn_cast(D)) + MC->mangleCXXCtor(CtorD, Ctor_Complete, OS); + else if (const auto *DtorD = dyn_cast(D)) + MC->mangleCXXDtor(DtorD, Dtor_Complete, OS); + else + MC->mangleName(D, OS); + return false; + } else { + IdentifierInfo *II = D->getIdentifier(); + if (!II) + return true; + OS << II->getName(); + return false; + } + } + + void writeObjCClassName(const ObjCInterfaceDecl *D, raw_ostream &OS) { + OS << getClassSymbolPrefix(); + OS << D->getObjCRuntimeNameAsString(); + } + + static StringRef getClassSymbolPrefix() { + return "OBJC_CLASS_$_"; + } + + std::string getMangledStructor(const NamedDecl *ND, unsigned StructorType) { + std::string FrontendBuf; + llvm::raw_string_ostream FOS(FrontendBuf); + + if (const auto *CD = dyn_cast_or_null(ND)) + MC->mangleCXXCtor(CD, static_cast(StructorType), FOS); + else if (const auto *DD = dyn_cast_or_null(ND)) + MC->mangleCXXDtor(DD, static_cast(StructorType), FOS); + + std::string BackendBuf; + llvm::raw_string_ostream BOS(BackendBuf); + + llvm::Mangler::getNameWithPrefix(BOS, FOS.str(), DL); + + return BOS.str(); + } + + std::string getMangledThunk(const CXXMethodDecl *MD, const ThunkInfo &T) { + std::string FrontendBuf; + llvm::raw_string_ostream FOS(FrontendBuf); + + MC->mangleThunk(MD, T, FOS); + + std::string BackendBuf; + llvm::raw_string_ostream BOS(BackendBuf); + + llvm::Mangler::getNameWithPrefix(BOS, FOS.str(), DL); + + return BOS.str(); + } +}; + +CodegenNameGenerator::CodegenNameGenerator(ASTContext &Ctx) + : Impl(new Implementation(Ctx)) { +} + +CodegenNameGenerator::~CodegenNameGenerator() { +} + +bool CodegenNameGenerator::writeName(const Decl *D, raw_ostream &OS) { + return Impl->writeName(D, OS); +} + +std::string CodegenNameGenerator::getName(const Decl *D) { + return Impl->getName(D); +} + +std::vector CodegenNameGenerator::getAllManglings(const Decl *D) { + return Impl->getAllManglings(D); +} diff --git a/lib/Index/IndexingAction.cpp b/lib/Index/IndexingAction.cpp index 46c96a35b6f..d7442931523 100644 --- a/lib/Index/IndexingAction.cpp +++ b/lib/Index/IndexingAction.cpp @@ -50,6 +50,7 @@ class IndexASTConsumer : public ASTConsumer { protected: void Initialize(ASTContext &Context) override { IndexCtx.setASTContext(Context); + IndexCtx.getDataConsumer().initialize(Context); } bool HandleTopLevelDecl(DeclGroupRef DG) override { @@ -170,5 +171,6 @@ void index::indexASTUnit(ASTUnit &Unit, IndexingOptions Opts) { IndexingContext IndexCtx(Opts, *DataConsumer); IndexCtx.setASTContext(Unit.getASTContext()); + DataConsumer->initialize(Unit.getASTContext()); indexTranslationUnit(Unit, IndexCtx); } diff --git a/test/Index/Core/index-source.m b/test/Index/Core/index-source.m index 42c23505b1b..b5a86fbcb4a 100644 --- a/test/Index/Core/index-source.m +++ b/test/Index/Core/index-source.m @@ -1,8 +1,8 @@ // RUN: c-index-test core -print-source-symbols -- %s | FileCheck %s @interface Base -// CHECK: [[@LINE-1]]:12 | objc-class/ObjC | Base | c:objc(cs)Base | Decl | rel: 0 +// CHECK: [[@LINE-1]]:12 | objc-class/ObjC | Base | c:objc(cs)Base | _OBJC_CLASS_$_Base | Decl | rel: 0 -(void)meth; -// CHECK: [[@LINE-1]]:1 | objc-instance-method/ObjC | meth | c:objc(cs)Base(im)meth | Decl/Dyn/RelChild | rel: 1 +// CHECK: [[@LINE-1]]:1 | objc-instance-method/ObjC | meth | c:objc(cs)Base(im)meth | -[Base meth] | Decl/Dyn/RelChild | rel: 1 // CHECK-NEXT: RelChild | Base | c:objc(cs)Base @end diff --git a/tools/c-index-test/core_main.cpp b/tools/c-index-test/core_main.cpp index c56d66f2147..e37b3422ac6 100644 --- a/tools/c-index-test/core_main.cpp +++ b/tools/c-index-test/core_main.cpp @@ -14,6 +14,7 @@ #include "clang/Index/IndexingAction.h" #include "clang/Index/IndexDataConsumer.h" #include "clang/Index/USRGeneration.h" +#include "clang/Index/CodegenNameGenerator.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Signals.h" #include "llvm/Support/raw_ostream.h" @@ -60,11 +61,16 @@ namespace { class PrintIndexDataConsumer : public IndexDataConsumer { raw_ostream &OS; + std::unique_ptr CGNameGen; public: PrintIndexDataConsumer(raw_ostream &OS) : OS(OS) { } + void initialize(ASTContext &Ctx) override { + CGNameGen.reset(new CodegenNameGenerator(Ctx)); + } + bool handleDeclOccurence(const Decl *D, SymbolRoleSet Roles, ArrayRef Relations, FileID FID, unsigned Offset, @@ -82,6 +88,10 @@ class PrintIndexDataConsumer : public IndexDataConsumer { printSymbolNameAndUSR(D, Ctx, OS); OS << " | "; + if (CGNameGen->writeName(D, OS)) + OS << ""; + OS << " | "; + printSymbolRoles(Roles, OS); OS << " | "; diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index ad162f0366c..49ae3ae3b2d 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -22,17 +22,15 @@ #include "CXType.h" #include "CursorVisitor.h" #include "clang/AST/Attr.h" -#include "clang/AST/Mangle.h" #include "clang/AST/StmtVisitor.h" -#include "clang/AST/VTableBuilder.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/DiagnosticCategories.h" #include "clang/Basic/DiagnosticIDs.h" -#include "clang/Basic/TargetInfo.h" #include "clang/Basic/Version.h" #include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Index/CodegenNameGenerator.h" #include "clang/Index/CommentToXML.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Lexer.h" @@ -43,8 +41,6 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Config/llvm-config.h" -#include "llvm/IR/DataLayout.h" -#include "llvm/IR/Mangler.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/Format.h" @@ -3949,59 +3945,6 @@ static SourceLocation getLocationFromExpr(const Expr *E) { return E->getLocStart(); } -static std::string getMangledStructor(std::unique_ptr &M, - std::unique_ptr &DL, - const NamedDecl *ND, - unsigned StructorType) { - std::string FrontendBuf; - llvm::raw_string_ostream FOS(FrontendBuf); - - if (const auto *CD = dyn_cast_or_null(ND)) - M->mangleCXXCtor(CD, static_cast(StructorType), FOS); - else if (const auto *DD = dyn_cast_or_null(ND)) - M->mangleCXXDtor(DD, static_cast(StructorType), FOS); - - std::string BackendBuf; - llvm::raw_string_ostream BOS(BackendBuf); - - llvm::Mangler::getNameWithPrefix(BOS, llvm::Twine(FOS.str()), *DL); - - return BOS.str(); -} - -static std::string getMangledName(std::unique_ptr &M, - std::unique_ptr &DL, - const NamedDecl *ND) { - std::string FrontendBuf; - llvm::raw_string_ostream FOS(FrontendBuf); - - M->mangleName(ND, FOS); - - std::string BackendBuf; - llvm::raw_string_ostream BOS(BackendBuf); - - llvm::Mangler::getNameWithPrefix(BOS, llvm::Twine(FOS.str()), *DL); - - return BOS.str(); -} - -static std::string getMangledThunk(std::unique_ptr &M, - std::unique_ptr &DL, - const CXXMethodDecl *MD, - const ThunkInfo &T) { - std::string FrontendBuf; - llvm::raw_string_ostream FOS(FrontendBuf); - - M->mangleThunk(MD, T, FOS); - - std::string BackendBuf; - llvm::raw_string_ostream BOS(BackendBuf); - - llvm::Mangler::getNameWithPrefix(BOS, llvm::Twine(FOS.str()), *DL); - - return BOS.str(); -} - extern "C" { unsigned clang_visitChildren(CXCursor parent, @@ -4350,29 +4293,9 @@ CXString clang_Cursor_getMangling(CXCursor C) { if (!D || !(isa(D) || isa(D))) return cxstring::createEmpty(); - // First apply frontend mangling. - const NamedDecl *ND = cast(D); - ASTContext &Ctx = ND->getASTContext(); - std::unique_ptr MC(Ctx.createMangleContext()); - - std::string FrontendBuf; - llvm::raw_string_ostream FrontendBufOS(FrontendBuf); - if (MC->shouldMangleDeclName(ND)) { - MC->mangleName(ND, FrontendBufOS); - } else { - ND->printName(FrontendBufOS); - } - - // Now apply backend mangling. - std::unique_ptr DL( - new llvm::DataLayout(Ctx.getTargetInfo().getDataLayoutString())); - - std::string FinalBuf; - llvm::raw_string_ostream FinalBufOS(FinalBuf); - llvm::Mangler::getNameWithPrefix(FinalBufOS, llvm::Twine(FrontendBufOS.str()), - *DL); - - return cxstring::createDup(FinalBufOS.str()); + ASTContext &Ctx = D->getASTContext(); + index::CodegenNameGenerator CGNameGen(Ctx); + return cxstring::createDup(CGNameGen.getName(D)); } CXStringSet *clang_Cursor_getCXXManglings(CXCursor C) { @@ -4383,49 +4306,9 @@ CXStringSet *clang_Cursor_getCXXManglings(CXCursor C) { if (!(isa(D) || isa(D))) return nullptr; - const NamedDecl *ND = cast(D); - - ASTContext &Ctx = ND->getASTContext(); - std::unique_ptr M(Ctx.createMangleContext()); - std::unique_ptr DL( - new llvm::DataLayout(Ctx.getTargetInfo().getDataLayoutString())); - - std::vector Manglings; - - auto hasDefaultCXXMethodCC = [](ASTContext &C, const CXXMethodDecl *MD) { - auto DefaultCC = C.getDefaultCallingConvention(/*IsVariadic=*/false, - /*IsCSSMethod=*/true); - auto CC = MD->getType()->getAs()->getCallConv(); - return CC == DefaultCC; - }; - - if (const auto *CD = dyn_cast_or_null(ND)) { - Manglings.emplace_back(getMangledStructor(M, DL, CD, Ctor_Base)); - - if (Ctx.getTargetInfo().getCXXABI().isItaniumFamily()) - if (!CD->getParent()->isAbstract()) - Manglings.emplace_back(getMangledStructor(M, DL, CD, Ctor_Complete)); - - if (Ctx.getTargetInfo().getCXXABI().isMicrosoft()) - if (CD->hasAttr() && CD->isDefaultConstructor()) - if (!(hasDefaultCXXMethodCC(Ctx, CD) && CD->getNumParams() == 0)) - Manglings.emplace_back(getMangledStructor(M, DL, CD, - Ctor_DefaultClosure)); - } else if (const auto *DD = dyn_cast_or_null(ND)) { - Manglings.emplace_back(getMangledStructor(M, DL, DD, Dtor_Base)); - if (Ctx.getTargetInfo().getCXXABI().isItaniumFamily()) { - Manglings.emplace_back(getMangledStructor(M, DL, DD, Dtor_Complete)); - if (DD->isVirtual()) - Manglings.emplace_back(getMangledStructor(M, DL, DD, Dtor_Deleting)); - } - } else if (const auto *MD = dyn_cast_or_null(ND)) { - Manglings.emplace_back(getMangledName(M, DL, ND)); - if (MD->isVirtual()) - if (const auto *TIV = Ctx.getVTableContext()->getThunkInfo(MD)) - for (const auto &T : *TIV) - Manglings.emplace_back(getMangledThunk(M, DL, MD, T)); - } - + ASTContext &Ctx = D->getASTContext(); + index::CodegenNameGenerator CGNameGen(Ctx); + std::vector Manglings = CGNameGen.getAllManglings(D); return cxstring::createSet(Manglings); } From 4c3dbd9ecdcd1d86d2ed9106dfeef6d04a02e188 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Sun, 14 Feb 2016 22:38:38 +0000 Subject: [PATCH 157/742] [test/Index] Set a specific target for the test. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260861 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/Index/Core/index-source.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Index/Core/index-source.m b/test/Index/Core/index-source.m index b5a86fbcb4a..0fdae7ae5f9 100644 --- a/test/Index/Core/index-source.m +++ b/test/Index/Core/index-source.m @@ -1,4 +1,4 @@ -// RUN: c-index-test core -print-source-symbols -- %s | FileCheck %s +// RUN: c-index-test core -print-source-symbols -- %s -target x86_64-apple-macosx10.7 | FileCheck %s @interface Base // CHECK: [[@LINE-1]]:12 | objc-class/ObjC | Base | c:objc(cs)Base | _OBJC_CLASS_$_Base | Decl | rel: 0 From 9185bce04926eadab684f5b11ecef90b014e597e Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Mon, 15 Feb 2016 00:36:52 +0000 Subject: [PATCH 158/742] silence -Wreturn-type warnings These codepaths would generate warnings with GCC on linux even though the switch was covered. Add llvm_unreachable markers to indicate that the switch should be covered. NFC. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260865 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Index/IndexSymbol.cpp | 3 +++ lib/Index/IndexingContext.cpp | 1 + tools/libclang/CXIndexDataConsumer.cpp | 3 +++ 3 files changed, 7 insertions(+) diff --git a/lib/Index/IndexSymbol.cpp b/lib/Index/IndexSymbol.cpp index bf3bfd1b95b..95ae9776826 100644 --- a/lib/Index/IndexSymbol.cpp +++ b/lib/Index/IndexSymbol.cpp @@ -266,6 +266,7 @@ StringRef index::getSymbolKindString(SymbolKind K) { case SymbolKind::CXXTypeAlias: return "type-alias"; case SymbolKind::CXXInterface: return "c++-__interface"; } + llvm_unreachable("invalid symbol kind"); } StringRef index::getTemplateKindStr(SymbolCXXTemplateKind TK) { @@ -275,6 +276,7 @@ StringRef index::getTemplateKindStr(SymbolCXXTemplateKind TK) { case SymbolCXXTemplateKind::TemplatePartialSpecialization : return "TPS"; case SymbolCXXTemplateKind::TemplateSpecialization: return "TS"; } + llvm_unreachable("invalid template kind"); } StringRef index::getSymbolLanguageString(SymbolLanguage K) { @@ -283,4 +285,5 @@ StringRef index::getSymbolLanguageString(SymbolLanguage K) { case SymbolLanguage::ObjC: return "ObjC"; case SymbolLanguage::CXX: return "C++"; } + llvm_unreachable("invalid symbol language kind"); } diff --git a/lib/Index/IndexingContext.cpp b/lib/Index/IndexingContext.cpp index 91fbbfb2684..9ac22f85e1d 100644 --- a/lib/Index/IndexingContext.cpp +++ b/lib/Index/IndexingContext.cpp @@ -135,6 +135,7 @@ bool IndexingContext::isTemplateImplicitInstantiation(const Decl *D) { case TSK_ExplicitInstantiationDefinition: return true; } + llvm_unreachable("invalid TemplateSpecializationKind"); } bool IndexingContext::shouldIgnoreIfImplicit(const Decl *D) { diff --git a/tools/libclang/CXIndexDataConsumer.cpp b/tools/libclang/CXIndexDataConsumer.cpp index f6f8e306794..322725ec654 100644 --- a/tools/libclang/CXIndexDataConsumer.cpp +++ b/tools/libclang/CXIndexDataConsumer.cpp @@ -1263,6 +1263,7 @@ static CXIdxEntityKind getEntityKindFromSymbolKind(SymbolKind K) { case SymbolKind::CXXTypeAlias: return CXIdxEntity_CXXTypeAlias; case SymbolKind::CXXInterface: return CXIdxEntity_CXXInterface; } + llvm_unreachable("invalid symbol kind"); } static CXIdxEntityCXXTemplateKind @@ -1275,6 +1276,7 @@ getEntityKindFromSymbolCXXTemplateKind(SymbolCXXTemplateKind K) { case SymbolCXXTemplateKind::TemplateSpecialization: return CXIdxEntity_TemplateSpecialization; } + llvm_unreachable("invalid template kind"); } static CXIdxEntityLanguage getEntityLangFromSymbolLang(SymbolLanguage L) { @@ -1283,4 +1285,5 @@ static CXIdxEntityLanguage getEntityLangFromSymbolLang(SymbolLanguage L) { case SymbolLanguage::ObjC: return CXIdxEntityLang_ObjC; case SymbolLanguage::CXX: return CXIdxEntityLang_CXX; } + llvm_unreachable("invalid symbol language"); } From 4c8b7b19b27f41a3810d0d94c4f77e0e25b8728e Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Mon, 15 Feb 2016 01:32:36 +0000 Subject: [PATCH 159/742] [AST/index] Introduce an option 'SuppressTemplateArgsInCXXConstructors' in printing policy. Enable it for USRs and names when indexing. Forward references can have different template argument names; including them makes USRs and names unstable, since the name depends on whether we saw a forward reference or not. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260866 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/PrettyPrinter.h | 7 ++++++- include/clang/Index/IndexSymbol.h | 5 +++++ lib/AST/DeclarationName.cpp | 8 +++++++- lib/Index/IndexSymbol.cpp | 19 +++++++++++++++++++ lib/Index/USRGeneration.cpp | 7 ++++++- test/Index/Core/index-source.cpp | 9 +++++++++ tools/c-index-test/core_main.cpp | 5 +---- 7 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 test/Index/Core/index-source.cpp diff --git a/include/clang/AST/PrettyPrinter.h b/include/clang/AST/PrettyPrinter.h index 8ab3f61703a..57495efa966 100644 --- a/include/clang/AST/PrettyPrinter.h +++ b/include/clang/AST/PrettyPrinter.h @@ -40,6 +40,7 @@ struct PrintingPolicy { SuppressUnwrittenScope(false), SuppressInitializers(false), ConstantArraySizeAsWritten(false), AnonymousTagLocations(true), SuppressStrongLifetime(false), SuppressLifetimeQualifiers(false), + SuppressTemplateArgsInCXXConstructors(false), Bool(LO.Bool), TerseOutput(false), PolishForDeclaration(false), Half(LO.Half), MSWChar(LO.MicrosoftExt && !LO.WChar), IncludeNewlines(true), MSVCFormatting(false) { } @@ -136,7 +137,11 @@ struct PrintingPolicy { /// \brief When true, suppress printing of lifetime qualifier in /// ARC. unsigned SuppressLifetimeQualifiers : 1; - + + /// When true, suppresses printing template arguments in names of C++ + /// constructors. + unsigned SuppressTemplateArgsInCXXConstructors : 1; + /// \brief Whether we can use 'bool' rather than '_Bool', even if the language /// doesn't actually have 'bool' (because, e.g., it is defined as a macro). unsigned Bool : 1; diff --git a/include/clang/Index/IndexSymbol.h b/include/clang/Index/IndexSymbol.h index feee13cdbf5..506540b0e6e 100644 --- a/include/clang/Index/IndexSymbol.h +++ b/include/clang/Index/IndexSymbol.h @@ -16,6 +16,7 @@ namespace clang { class Decl; + class LangOptions; namespace index { @@ -111,6 +112,10 @@ SymbolInfo getSymbolInfo(const Decl *D); void applyForEachSymbolRole(SymbolRoleSet Roles, llvm::function_ref Fn); void printSymbolRoles(SymbolRoleSet Roles, raw_ostream &OS); + +/// \returns true if no name was printed, false otherwise. +bool printSymbolName(const Decl *D, const LangOptions &LO, raw_ostream &OS); + StringRef getSymbolKindString(SymbolKind K); StringRef getTemplateKindStr(SymbolCXXTemplateKind TK); StringRef getSymbolLanguageString(SymbolLanguage K); diff --git a/lib/AST/DeclarationName.cpp b/lib/AST/DeclarationName.cpp index f6d045b2dab..344a2389228 100644 --- a/lib/AST/DeclarationName.cpp +++ b/lib/AST/DeclarationName.cpp @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// #include "clang/AST/ASTContext.h" -#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/DeclarationName.h" #include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" @@ -140,6 +140,12 @@ static void printCXXConstructorDestructorName(QualType ClassType, OS << *ClassRec->getDecl(); return; } + if (Policy.SuppressTemplateArgsInCXXConstructors) { + if (auto *InjTy = ClassType->getAs()) { + OS << *InjTy->getDecl(); + return; + } + } if (!Policy.LangOpts.CPlusPlus) { // Passed policy is the default one from operator <<, use a C++ policy. LangOptions LO; diff --git a/lib/Index/IndexSymbol.cpp b/lib/Index/IndexSymbol.cpp index 95ae9776826..010ccd42a4a 100644 --- a/lib/Index/IndexSymbol.cpp +++ b/lib/Index/IndexSymbol.cpp @@ -11,6 +11,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" +#include "clang/AST/PrettyPrinter.h" using namespace clang; using namespace clang::index; @@ -234,6 +235,24 @@ void index::printSymbolRoles(SymbolRoleSet Roles, raw_ostream &OS) { }); } +bool index::printSymbolName(const Decl *D, const LangOptions &LO, + raw_ostream &OS) { + if (auto *ND = dyn_cast(D)) { + PrintingPolicy Policy(LO); + // Forward references can have different template argument names. Suppress + // the template argument names in constructors to make their name more + // stable. + Policy.SuppressTemplateArgsInCXXConstructors = true; + DeclarationName DeclName = ND->getDeclName(); + if (DeclName.isEmpty()) + return true; + DeclName.print(OS, Policy); + return false; + } else { + return true; + } +} + StringRef index::getSymbolKindString(SymbolKind K) { switch (K) { case SymbolKind::Unknown: return ""; diff --git a/lib/Index/USRGeneration.cpp b/lib/Index/USRGeneration.cpp index c57694fc10a..3dfc27df42f 100644 --- a/lib/Index/USRGeneration.cpp +++ b/lib/Index/USRGeneration.cpp @@ -203,7 +203,12 @@ void USRGenerator::VisitFunctionDecl(const FunctionDecl *D) { VisitTemplateParameterList(FunTmpl->getTemplateParameters()); } else Out << "@F@"; - D->printName(Out); + + PrintingPolicy Policy(Context->getLangOpts()); + // Forward references can have different template argument names. Suppress the + // template argument names in constructors to make their USR more stable. + Policy.SuppressTemplateArgsInCXXConstructors = true; + D->getDeclName().print(Out, Policy); ASTContext &Ctx = *Context; if (!Ctx.getLangOpts().CPlusPlus || D->isExternC()) diff --git a/test/Index/Core/index-source.cpp b/test/Index/Core/index-source.cpp new file mode 100644 index 00000000000..75446468266 --- /dev/null +++ b/test/Index/Core/index-source.cpp @@ -0,0 +1,9 @@ +// RUN: c-index-test core -print-source-symbols -- %s -target x86_64-apple-macosx10.7 | FileCheck %s + +template +class TemplCls { +// CHECK: [[@LINE-1]]:7 | c++-class/C++ | TemplCls | c:@ST>1#T@TemplCls | | Def | rel: 0 + TemplCls(int x); + // CHECK: [[@LINE-1]]:3 | constructor/C++ | TemplCls | c:@ST>1#T@TemplCls@F@TemplCls#I# | | Decl/RelChild | rel: 1 + // CHECK-NEXT: RelChild | TemplCls | c:@ST>1#T@TemplCls +}; diff --git a/tools/c-index-test/core_main.cpp b/tools/c-index-test/core_main.cpp index e37b3422ac6..e72b9f93efd 100644 --- a/tools/c-index-test/core_main.cpp +++ b/tools/c-index-test/core_main.cpp @@ -156,10 +156,7 @@ static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS) { static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx, raw_ostream &OS) { - if (auto *ND = dyn_cast(D)) { - PrintingPolicy PrintPolicy(Ctx.getLangOpts()); - ND->getDeclName().print(OS, PrintPolicy); - } else { + if (printSymbolName(D, Ctx.getLangOpts(), OS)) { OS << ""; } OS << " | "; From b11ff4077f11aa9d190ecfee87711db1cc218def Mon Sep 17 00:00:00 2001 From: Xinliang David Li Date: Wed, 20 Jan 2016 00:24:52 +0000 Subject: [PATCH 160/742] Reference the updated function name /NFC git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258261 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit b5997a7e7464a6ffc571b05d2fff6b09a4eef977) --- lib/CodeGen/CoverageMappingGen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CodeGen/CoverageMappingGen.cpp b/lib/CodeGen/CoverageMappingGen.cpp index 75b85ed8d51..9279d1a37cf 100644 --- a/lib/CodeGen/CoverageMappingGen.cpp +++ b/lib/CodeGen/CoverageMappingGen.cpp @@ -1044,7 +1044,7 @@ void CoverageMappingModuleGen::emit() { // to pass the list of names referenced to codegen. new llvm::GlobalVariable(CGM.getModule(), NamesArrTy, true, llvm::GlobalValue::InternalLinkage, NamesArrVal, - llvm::getCoverageNamesVarName()); + llvm::getCoverageUnusedNamesVarName()); } } From d9809a260be59170ca45a6cabd30a41c97777ea2 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 9 Feb 2016 01:05:04 +0000 Subject: [PATCH 161/742] Fix undefined behavior when compiling in C++14 due to sized operator delete being called with the wrong size: convert CGFunctionInfo to use TrailingObjects and ask TrailingObjects to provide a working 'operator delete' for us. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260181 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 0f7f466fd0081ad90040e4b5df0a1611b7911269) --- include/clang/CodeGen/CGFunctionInfo.h | 26 +++++++++++++++++++------- lib/CodeGen/CGCall.cpp | 3 +-- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/include/clang/CodeGen/CGFunctionInfo.h b/include/clang/CodeGen/CGFunctionInfo.h index bb6ceb43514..4c9e0134ffb 100644 --- a/include/clang/CodeGen/CGFunctionInfo.h +++ b/include/clang/CodeGen/CGFunctionInfo.h @@ -20,6 +20,7 @@ #include "clang/AST/CharUnits.h" #include "clang/AST/Type.h" #include "llvm/ADT/FoldingSet.h" +#include "llvm/Support/TrailingObjects.h" #include namespace llvm { @@ -331,13 +332,19 @@ class RequiredArgs { } }; +// Implementation detail of CGFunctionInfo, factored out so it can be named +// in the TrailingObjects base class of CGFunctionInfo. +struct CGFunctionInfoArgInfo { + CanQualType type; + ABIArgInfo info; +}; + /// CGFunctionInfo - Class to encapsulate the information about a /// function definition. -class CGFunctionInfo : public llvm::FoldingSetNode { - struct ArgInfo { - CanQualType type; - ABIArgInfo info; - }; +class CGFunctionInfo final + : public llvm::FoldingSetNode, + private llvm::TrailingObjects { + typedef CGFunctionInfoArgInfo ArgInfo; /// The LLVM::CallingConv to use for this function (as specified by the /// user). @@ -374,13 +381,17 @@ class CGFunctionInfo : public llvm::FoldingSetNode { unsigned ArgStructAlign; unsigned NumArgs; + ArgInfo *getArgsBuffer() { - return reinterpret_cast(this+1); + return getTrailingObjects(); } const ArgInfo *getArgsBuffer() const { - return reinterpret_cast(this + 1); + return getTrailingObjects(); } + size_t numTrailingObjects(OverloadToken) { return NumArgs + 1; } + friend class TrailingObjects; + CGFunctionInfo() : Required(RequiredArgs::All) {} public: @@ -391,6 +402,7 @@ class CGFunctionInfo : public llvm::FoldingSetNode { CanQualType resultType, ArrayRef argTypes, RequiredArgs required); + void operator delete(void *p) { TrailingObjects::operator delete(p); } typedef const ArgInfo *const_arg_iterator; typedef ArgInfo *arg_iterator; diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index 715e8e7aadc..7a24e4f0bf1 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -569,8 +569,7 @@ CGFunctionInfo *CGFunctionInfo::create(unsigned llvmCC, CanQualType resultType, ArrayRef argTypes, RequiredArgs required) { - void *buffer = operator new(sizeof(CGFunctionInfo) + - sizeof(ArgInfo) * (argTypes.size() + 1)); + void *buffer = operator new(totalSizeToAlloc(argTypes.size() + 1)); CGFunctionInfo *FI = new(buffer) CGFunctionInfo(); FI->CallingConvention = llvmCC; FI->EffectiveCallingConvention = llvmCC; From 676bab5ab024592cb9189a0f5bbf61a1f3c632dc Mon Sep 17 00:00:00 2001 From: Xinliang David Li Date: Fri, 5 Feb 2016 23:36:08 +0000 Subject: [PATCH 162/742] [PGO] Test case update Temporarily relax check in test to avoid breakage for format change in LLVM side. Once that is done, the test case will be retightened. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259955 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit bd295c26bf3d0d7ee4dfbf432e9506a988e12ae5) --- test/CoverageMapping/ir.c | 2 +- test/CoverageMapping/unused_names.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/CoverageMapping/ir.c b/test/CoverageMapping/ir.c index f94d34c0b98..a9c14399b96 100644 --- a/test/CoverageMapping/ir.c +++ b/test/CoverageMapping/ir.c @@ -9,4 +9,4 @@ int main(void) { return 0; } -// CHECK: @__llvm_coverage_mapping = internal constant { { i32, i32, i32, i32 }, [2 x <{ i8*, i32, i32, i64 }>], [{{[0-9]+}} x i8] } { { i32, i32, i32, i32 } { i32 2, i32 {{[0-9]+}}, i32 {{[0-9]+}}, i32 0 }, [2 x <{ i8*, i32, i32, i64 }>] [<{ i8*, i32, i32, i64 }> <{ i8* getelementptr inbounds ([3 x i8], [3 x i8]* @__profn_foo, i32 0, i32 0), i32 3, i32 9, i64 {{[0-9]+}} }>, <{ i8*, i32, i32, i64 }> <{ i8* getelementptr inbounds ([4 x i8], [4 x i8]* @__profn_main, i32 0, i32 0), i32 4, i32 9, i64 {{[0-9]+}} }>] +// CHECK: @__llvm_coverage_mapping = internal constant { { i32, i32, i32, i32 }, [2 x <{{.*}}>], [{{[0-9]+}} x i8] } { { i32, i32, i32, i32 } { i32 2, i32 {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}} }, [2 x <{{.*}}>] [<{{.*}}> <{{.*}}>, <{{.*}}> <{{.*}}>] diff --git a/test/CoverageMapping/unused_names.c b/test/CoverageMapping/unused_names.c index 00941b86315..c0c10ea87c7 100644 --- a/test/CoverageMapping/unused_names.c +++ b/test/CoverageMapping/unused_names.c @@ -4,9 +4,9 @@ // Since foo is never emitted, there should not be a profile name for it. -// CHECK-DAG: @__profn_bar = {{.*}} [3 x i8] c"bar", section "{{.*}}__llvm_prf_names" -// CHECK-DAG: @__profn_baz = {{.*}} [3 x i8] c"baz", section "{{.*}}__llvm_prf_names" -// CHECK-DAG: @__profn_unused_names.c_qux = {{.*}} [18 x i8] c"unused_names.c:qux", section "{{.*}}__llvm_prf_names" +// CHECK-DAG: @__profn_bar = {{.*}} [3 x i8] c"bar" +// CHECK-DAG: @__profn_baz = {{.*}} [3 x i8] c"baz" +// CHECK-DAG: @__profn_unused_names.c_qux = {{.*}} [18 x i8] c"unused_names.c:qux" // SYSHEADER-NOT: @__profn_foo = From c6868f42e7171a4e2a9e9e134f4dcbc8f92b974f Mon Sep 17 00:00:00 2001 From: Xinliang David Li Date: Tue, 9 Feb 2016 20:02:59 +0000 Subject: [PATCH 163/742] [PGO] Fix issue: explicitly defaulted assignop is not profiled Differential Revision: http://reviews.llvm.org/D16947 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260270 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 424e3d77320e85679ae20859d1aff5cbe44c32df) --- lib/CodeGen/CGClass.cpp | 1 + test/Profile/def-assignop.cpp | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 test/Profile/def-assignop.cpp diff --git a/lib/CodeGen/CGClass.cpp b/lib/CodeGen/CGClass.cpp index faf570c5126..41f93f421ab 100644 --- a/lib/CodeGen/CGClass.cpp +++ b/lib/CodeGen/CGClass.cpp @@ -1608,6 +1608,7 @@ void CodeGenFunction::emitImplicitAssignmentOperatorBody(FunctionArgList &Args) LexicalScope Scope(*this, RootCS->getSourceRange()); + incrementProfileCounter(RootCS); AssignmentMemcpyizer AM(*this, AssignOp, Args); for (auto *I : RootCS->body()) AM.emitAssignment(I); diff --git a/test/Profile/def-assignop.cpp b/test/Profile/def-assignop.cpp new file mode 100644 index 00000000000..50537f0e055 --- /dev/null +++ b/test/Profile/def-assignop.cpp @@ -0,0 +1,32 @@ +// RUN: %clang_cc1 -x c++ -std=c++11 %s -triple x86_64-unknown-linux-gnu -main-file-name def-assignop.cpp -o - -emit-llvm -fprofile-instrument=clang | FileCheck --check-prefix=PGOGEN %s +// RUN: %clang_cc1 -x c++ -std=c++11 %s -triple x86_64-unknown-linux-gnu -main-file-name def-assignop.cpp -o - -emit-llvm -fprofile-instrument=clang -fcoverage-mapping | FileCheck --check-prefix=COVMAP %s + +struct B { + B& operator=(const B &b); + B& operator=(const B &&b); +}; + +struct A { + A &operator=(const A &) = default; + // PGOGEN: define {{.*}}@_ZN1AaSERKS_( + // PGOGEN: %pgocount = load {{.*}} @__profc__ZN1AaSERKS_ + // PGOGEN: {{.*}}add{{.*}}%pgocount, 1 + // PGOGEN: store{{.*}}@__profc__ZN1AaSERKS_ + A &operator=(A &&) = default; + // PGOGEN: define {{.*}}@_ZN1AaSEOS_ + // PGOGEN: %pgocount = load {{.*}} @__profc__ZN1AaSEOS_ + // PGOGEN: {{.*}}add{{.*}}%pgocount, 1 + // PGOGEN: store{{.*}}@__profc__ZN1AaSEOS_ + + // Check that coverage mapping includes 6 function records including the + // defaulted copy and move operators: A::operator= + // COVMAP: @__llvm_coverage_mapping = {{.*}} { { i32, i32, i32, i32 }, [3 x <{{.*}}>], + B b; +}; + +int main() { + A a1, a2; + a1 = a2; + a2 = static_cast(a1); + return 0; +} From dfc34861a6ef4ff117b0cc0a12247e3830bf03d9 Mon Sep 17 00:00:00 2001 From: Vedant Kumar Date: Mon, 15 Feb 2016 15:12:46 -0800 Subject: [PATCH 164/742] Revert "Fix undefined behavior when compiling in C++14 due to sized operator delete" This reverts commit d9809a260be59170ca45a6cabd30a41c97777ea2. Due to error: no member named 'operator delete' in 'llvm::TrailingObjects' --- include/clang/CodeGen/CGFunctionInfo.h | 26 +++++++------------------- lib/CodeGen/CGCall.cpp | 3 ++- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/include/clang/CodeGen/CGFunctionInfo.h b/include/clang/CodeGen/CGFunctionInfo.h index 4c9e0134ffb..bb6ceb43514 100644 --- a/include/clang/CodeGen/CGFunctionInfo.h +++ b/include/clang/CodeGen/CGFunctionInfo.h @@ -20,7 +20,6 @@ #include "clang/AST/CharUnits.h" #include "clang/AST/Type.h" #include "llvm/ADT/FoldingSet.h" -#include "llvm/Support/TrailingObjects.h" #include namespace llvm { @@ -332,19 +331,13 @@ class RequiredArgs { } }; -// Implementation detail of CGFunctionInfo, factored out so it can be named -// in the TrailingObjects base class of CGFunctionInfo. -struct CGFunctionInfoArgInfo { - CanQualType type; - ABIArgInfo info; -}; - /// CGFunctionInfo - Class to encapsulate the information about a /// function definition. -class CGFunctionInfo final - : public llvm::FoldingSetNode, - private llvm::TrailingObjects { - typedef CGFunctionInfoArgInfo ArgInfo; +class CGFunctionInfo : public llvm::FoldingSetNode { + struct ArgInfo { + CanQualType type; + ABIArgInfo info; + }; /// The LLVM::CallingConv to use for this function (as specified by the /// user). @@ -381,17 +374,13 @@ class CGFunctionInfo final unsigned ArgStructAlign; unsigned NumArgs; - ArgInfo *getArgsBuffer() { - return getTrailingObjects(); + return reinterpret_cast(this+1); } const ArgInfo *getArgsBuffer() const { - return getTrailingObjects(); + return reinterpret_cast(this + 1); } - size_t numTrailingObjects(OverloadToken) { return NumArgs + 1; } - friend class TrailingObjects; - CGFunctionInfo() : Required(RequiredArgs::All) {} public: @@ -402,7 +391,6 @@ class CGFunctionInfo final CanQualType resultType, ArrayRef argTypes, RequiredArgs required); - void operator delete(void *p) { TrailingObjects::operator delete(p); } typedef const ArgInfo *const_arg_iterator; typedef ArgInfo *arg_iterator; diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index 7a24e4f0bf1..715e8e7aadc 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -569,7 +569,8 @@ CGFunctionInfo *CGFunctionInfo::create(unsigned llvmCC, CanQualType resultType, ArrayRef argTypes, RequiredArgs required) { - void *buffer = operator new(totalSizeToAlloc(argTypes.size() + 1)); + void *buffer = operator new(sizeof(CGFunctionInfo) + + sizeof(ArgInfo) * (argTypes.size() + 1)); CGFunctionInfo *FI = new(buffer) CGFunctionInfo(); FI->CallingConvention = llvmCC; FI->EffectiveCallingConvention = llvmCC; From 09e9df636c1f2c2647e68f0741c79c755992415c Mon Sep 17 00:00:00 2001 From: Vedant Kumar Date: Mon, 15 Feb 2016 15:24:00 -0800 Subject: [PATCH 165/742] Revert "[PGO] Fix issue: explicitly defaulted assignop is not profiled" This reverts commit c6868f42e7171a4e2a9e9e134f4dcbc8f92b974f. Note: The RUN line depends on some future changes. Will revert, fix up the RUN line, test, and re-commit. --- lib/CodeGen/CGClass.cpp | 1 - test/Profile/def-assignop.cpp | 32 -------------------------------- 2 files changed, 33 deletions(-) delete mode 100644 test/Profile/def-assignop.cpp diff --git a/lib/CodeGen/CGClass.cpp b/lib/CodeGen/CGClass.cpp index 41f93f421ab..faf570c5126 100644 --- a/lib/CodeGen/CGClass.cpp +++ b/lib/CodeGen/CGClass.cpp @@ -1608,7 +1608,6 @@ void CodeGenFunction::emitImplicitAssignmentOperatorBody(FunctionArgList &Args) LexicalScope Scope(*this, RootCS->getSourceRange()); - incrementProfileCounter(RootCS); AssignmentMemcpyizer AM(*this, AssignOp, Args); for (auto *I : RootCS->body()) AM.emitAssignment(I); diff --git a/test/Profile/def-assignop.cpp b/test/Profile/def-assignop.cpp deleted file mode 100644 index 50537f0e055..00000000000 --- a/test/Profile/def-assignop.cpp +++ /dev/null @@ -1,32 +0,0 @@ -// RUN: %clang_cc1 -x c++ -std=c++11 %s -triple x86_64-unknown-linux-gnu -main-file-name def-assignop.cpp -o - -emit-llvm -fprofile-instrument=clang | FileCheck --check-prefix=PGOGEN %s -// RUN: %clang_cc1 -x c++ -std=c++11 %s -triple x86_64-unknown-linux-gnu -main-file-name def-assignop.cpp -o - -emit-llvm -fprofile-instrument=clang -fcoverage-mapping | FileCheck --check-prefix=COVMAP %s - -struct B { - B& operator=(const B &b); - B& operator=(const B &&b); -}; - -struct A { - A &operator=(const A &) = default; - // PGOGEN: define {{.*}}@_ZN1AaSERKS_( - // PGOGEN: %pgocount = load {{.*}} @__profc__ZN1AaSERKS_ - // PGOGEN: {{.*}}add{{.*}}%pgocount, 1 - // PGOGEN: store{{.*}}@__profc__ZN1AaSERKS_ - A &operator=(A &&) = default; - // PGOGEN: define {{.*}}@_ZN1AaSEOS_ - // PGOGEN: %pgocount = load {{.*}} @__profc__ZN1AaSEOS_ - // PGOGEN: {{.*}}add{{.*}}%pgocount, 1 - // PGOGEN: store{{.*}}@__profc__ZN1AaSEOS_ - - // Check that coverage mapping includes 6 function records including the - // defaulted copy and move operators: A::operator= - // COVMAP: @__llvm_coverage_mapping = {{.*}} { { i32, i32, i32, i32 }, [3 x <{{.*}}>], - B b; -}; - -int main() { - A a1, a2; - a1 = a2; - a2 = static_cast(a1); - return 0; -} From 619e95dea8a1ca0172ceeeb6912fd46363344622 Mon Sep 17 00:00:00 2001 From: Xinliang David Li Date: Tue, 9 Feb 2016 20:02:59 +0000 Subject: [PATCH 166/742] [PGO] Fix issue: explicitly defaulted assignop is not profiled Differential Revision: http://reviews.llvm.org/D16947 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260270 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 424e3d77320e85679ae20859d1aff5cbe44c32df) --- lib/CodeGen/CGClass.cpp | 1 + test/Profile/def-assignop.cpp | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 test/Profile/def-assignop.cpp diff --git a/lib/CodeGen/CGClass.cpp b/lib/CodeGen/CGClass.cpp index faf570c5126..41f93f421ab 100644 --- a/lib/CodeGen/CGClass.cpp +++ b/lib/CodeGen/CGClass.cpp @@ -1608,6 +1608,7 @@ void CodeGenFunction::emitImplicitAssignmentOperatorBody(FunctionArgList &Args) LexicalScope Scope(*this, RootCS->getSourceRange()); + incrementProfileCounter(RootCS); AssignmentMemcpyizer AM(*this, AssignOp, Args); for (auto *I : RootCS->body()) AM.emitAssignment(I); diff --git a/test/Profile/def-assignop.cpp b/test/Profile/def-assignop.cpp new file mode 100644 index 00000000000..50537f0e055 --- /dev/null +++ b/test/Profile/def-assignop.cpp @@ -0,0 +1,32 @@ +// RUN: %clang_cc1 -x c++ -std=c++11 %s -triple x86_64-unknown-linux-gnu -main-file-name def-assignop.cpp -o - -emit-llvm -fprofile-instrument=clang | FileCheck --check-prefix=PGOGEN %s +// RUN: %clang_cc1 -x c++ -std=c++11 %s -triple x86_64-unknown-linux-gnu -main-file-name def-assignop.cpp -o - -emit-llvm -fprofile-instrument=clang -fcoverage-mapping | FileCheck --check-prefix=COVMAP %s + +struct B { + B& operator=(const B &b); + B& operator=(const B &&b); +}; + +struct A { + A &operator=(const A &) = default; + // PGOGEN: define {{.*}}@_ZN1AaSERKS_( + // PGOGEN: %pgocount = load {{.*}} @__profc__ZN1AaSERKS_ + // PGOGEN: {{.*}}add{{.*}}%pgocount, 1 + // PGOGEN: store{{.*}}@__profc__ZN1AaSERKS_ + A &operator=(A &&) = default; + // PGOGEN: define {{.*}}@_ZN1AaSEOS_ + // PGOGEN: %pgocount = load {{.*}} @__profc__ZN1AaSEOS_ + // PGOGEN: {{.*}}add{{.*}}%pgocount, 1 + // PGOGEN: store{{.*}}@__profc__ZN1AaSEOS_ + + // Check that coverage mapping includes 6 function records including the + // defaulted copy and move operators: A::operator= + // COVMAP: @__llvm_coverage_mapping = {{.*}} { { i32, i32, i32, i32 }, [3 x <{{.*}}>], + B b; +}; + +int main() { + A a1, a2; + a1 = a2; + a2 = static_cast(a1); + return 0; +} From bf1944bc29e06a019bf04df1f65ac6fc734c82e7 Mon Sep 17 00:00:00 2001 From: Vedant Kumar Date: Mon, 15 Feb 2016 15:32:04 -0800 Subject: [PATCH 167/742] [Profile] Fix RUN line for def-assignop.cpp --- test/Profile/def-assignop.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Profile/def-assignop.cpp b/test/Profile/def-assignop.cpp index 50537f0e055..0a36ff8fbcd 100644 --- a/test/Profile/def-assignop.cpp +++ b/test/Profile/def-assignop.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -x c++ -std=c++11 %s -triple x86_64-unknown-linux-gnu -main-file-name def-assignop.cpp -o - -emit-llvm -fprofile-instrument=clang | FileCheck --check-prefix=PGOGEN %s -// RUN: %clang_cc1 -x c++ -std=c++11 %s -triple x86_64-unknown-linux-gnu -main-file-name def-assignop.cpp -o - -emit-llvm -fprofile-instrument=clang -fcoverage-mapping | FileCheck --check-prefix=COVMAP %s +// RUN: %clang_cc1 -x c++ -std=c++11 %s -triple x86_64-unknown-linux-gnu -main-file-name def-assignop.cpp -o - -emit-llvm -fprofile-instr-generate | FileCheck --check-prefix=PGOGEN %s +// RUN: %clang_cc1 -x c++ -std=c++11 %s -triple x86_64-unknown-linux-gnu -main-file-name def-assignop.cpp -o - -emit-llvm -fprofile-instr-generate -fcoverage-mapping | FileCheck --check-prefix=COVMAP %s struct B { B& operator=(const B &b); From e47ab27899f1188ac73b833da790c86ea20e5ba5 Mon Sep 17 00:00:00 2001 From: Xinliang David Li Date: Thu, 28 Jan 2016 18:25:53 +0000 Subject: [PATCH 168/742] [PGO] test case cleanups 1. Make test case more focused and robust by focusing on what to be tested (linkage, icall) -- make it easier to validate 2. Testing linkages of data and counter variables instead of names. Counters and data are more relavant to be tested. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259067 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit c599c9b4378f4d99500959895159e4dd26980ee6) Note: Remove clang value-profiling related test case (c-indirect-call.c) --- test/Profile/c-linkage-available_externally.c | 8 +++----- test/Profile/c-linkage.c | 14 +++++++++----- test/Profile/cxx-linkage.cpp | 12 ++++++++---- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/test/Profile/c-linkage-available_externally.c b/test/Profile/c-linkage-available_externally.c index 8585bf8425b..61a258636be 100644 --- a/test/Profile/c-linkage-available_externally.c +++ b/test/Profile/c-linkage-available_externally.c @@ -1,11 +1,9 @@ -// Make sure instrementation data from available_externally functions doesn't -// get thrown out. +// Make sure instrumentation data from available_externally functions doesn't +// get thrown out and are emitted with the expected linkage. // RUN: %clang_cc1 -O2 -triple x86_64-apple-macosx10.9 -main-file-name c-linkage-available_externally.c %s -o - -emit-llvm -fprofile-instr-generate | FileCheck %s -// CHECK: @__profn_foo = linkonce_odr hidden constant [3 x i8] c"foo", section "__DATA,__llvm_prf_names", align 1 - // CHECK: @__profc_foo = linkonce_odr hidden global [1 x i64] zeroinitializer, section "__DATA,__llvm_prf_cnts", align 8 -// CHECK: @__profd_foo = linkonce_odr hidden global { i32, i32, i64, i8*, i64*, i8*, i8*, [1 x i16] } { i32 3, i32 1, i64 0, i8* getelementptr inbounds ([3 x i8], [3 x i8]* @__profn_foo, i32 0, i32 0), i64* getelementptr inbounds ([1 x i64], [1 x i64]* @__profc_foo, i32 0, i32 0), i8* null, i8* null, [1 x i16] zeroinitializer }, section "__DATA,__llvm_prf_data", align 8 +// CHECK: @__profd_foo = linkonce_odr hidden global {{.*}} i64* getelementptr inbounds ([1 x i64], [1 x i64]* @__profc_foo, i32 0, i32 0){{.*}}, section "__DATA,__llvm_prf_data", align 8 inline int foo(void) { return 1; } int main(void) { diff --git a/test/Profile/c-linkage.c b/test/Profile/c-linkage.c index e6fbda9c288..c82dcabb60a 100644 --- a/test/Profile/c-linkage.c +++ b/test/Profile/c-linkage.c @@ -1,10 +1,14 @@ -// Check that the profiling names we create have the linkage we expect +// Check that the profiling counters and data we create have the linkage we expect // RUN: %clang_cc1 -triple x86_64-apple-macosx10.9 -main-file-name c-linkage.c %s -o - -emit-llvm -fprofile-instr-generate | FileCheck %s -// CHECK: @__profn_foo = private constant [3 x i8] c"foo" -// CHECK: @__profn_foo_weak = weak hidden constant [8 x i8] c"foo_weak" -// CHECK: @__profn_main = private constant [4 x i8] c"main" -// CHECK: @__profn_c_linkage.c_foo_internal = private constant [24 x i8] c"c-linkage.c:foo_internal" +// CHECK: @__profc_foo = private global +// CHECK: @__profd_foo = private global +// CHECK: @__profc_foo_weak = weak hidden global +// CHECK: @__profd_foo_weak = weak hidden global +// CHECK: @__profc_main = private global +// CHECK: @__profd_main = private global +// CHECK: @__profc_c_linkage.c_foo_internal = private global +// CHECK: @__profd_c_linkage.c_foo_internal = private global void foo(void) { } diff --git a/test/Profile/cxx-linkage.cpp b/test/Profile/cxx-linkage.cpp index 701a8802815..55934037ba5 100644 --- a/test/Profile/cxx-linkage.cpp +++ b/test/Profile/cxx-linkage.cpp @@ -1,9 +1,13 @@ // RUN: %clang_cc1 -triple x86_64-apple-macosx10.9.0 -emit-llvm -main-file-name cxx-linkage.cpp %s -o - -fprofile-instr-generate | FileCheck %s -// CHECK: @__profn__Z3foov = private constant [7 x i8] c"_Z3foov" -// CHECK: @__profn__Z8foo_weakv = weak hidden constant [12 x i8] c"_Z8foo_weakv" -// CHECK: @__profn_main = private constant [4 x i8] c"main" -// CHECK: @__profn__Z10foo_inlinev = linkonce_odr hidden constant [15 x i8] c"_Z10foo_inlinev" +// CHECK: @__profc__Z3foov = private global +// CHECK: @__profd__Z3foov = private global +// CHECK: @__profc__Z8foo_weakv = weak hidden global +// CHECK: @__profd__Z8foo_weakv = weak hidden global +// CHECK: @__profc_main = private global +// CHECK: @__profd_main = private global +// CHECK: @__profc__Z10foo_inlinev = linkonce_odr hidden global +// CHECK: @__profd__Z10foo_inlinev = linkonce_odr hidden global void foo(void) { } From db847e681f240fe24fc63dc61afb1580ac33bc49 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Tue, 16 Feb 2016 05:39:33 +0000 Subject: [PATCH 169/742] [Frontend] Make sure WrapperFrontendAction updates CurrentInput after calling BeginSourceFileAction. I don't have a test case to add unfortunately. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260937 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Frontend/FrontendAction.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/Frontend/FrontendAction.cpp b/lib/Frontend/FrontendAction.cpp index 71fb17c25f5..82373e320ef 100644 --- a/lib/Frontend/FrontendAction.cpp +++ b/lib/Frontend/FrontendAction.cpp @@ -566,7 +566,10 @@ bool WrapperFrontendAction::BeginSourceFileAction(CompilerInstance &CI, StringRef Filename) { WrappedAction->setCurrentInput(getCurrentInput()); WrappedAction->setCompilerInstance(&CI); - return WrappedAction->BeginSourceFileAction(CI, Filename); + auto Ret = WrappedAction->BeginSourceFileAction(CI, Filename); + // BeginSourceFileAction may change CurrentInput, e.g. during module builds. + setCurrentInput(WrappedAction->getCurrentInput()); + return Ret; } void WrapperFrontendAction::ExecuteAction() { WrappedAction->ExecuteAction(); From b25685f63f2a8548066570b2fa0440bdfa31b9f7 Mon Sep 17 00:00:00 2001 From: Vedant Kumar Date: Tue, 16 Feb 2016 02:14:44 +0000 Subject: [PATCH 170/742] Simplify users of StringRef::{l,r}trim (clang) (NFC) r260925 introduced a version of the *trim methods which is preferable when trimming a single kind of character. Update all users in clang. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260927 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit ce4ded5b5870d1fb21bf75a97621fa9404be3170) --- lib/Lex/PPDirectives.cpp | 2 +- lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp | 2 +- lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Lex/PPDirectives.cpp b/lib/Lex/PPDirectives.cpp index 105288b728d..e5cf43e1717 100644 --- a/lib/Lex/PPDirectives.cpp +++ b/lib/Lex/PPDirectives.cpp @@ -1229,7 +1229,7 @@ void Preprocessor::HandleUserDiagnosticDirective(Token &Tok, // Find the first non-whitespace character, so that we can make the // diagnostic more succinct. - StringRef Msg = StringRef(Message).ltrim(" "); + StringRef Msg = StringRef(Message).ltrim(' '); if (isWarning) Diag(Tok, diag::pp_hash_warning) << Msg; diff --git a/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp b/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp index 504b8b30187..a25f3e01c91 100644 --- a/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp @@ -1000,7 +1000,7 @@ void EmptyLocalizationContextChecker::MethodCrawler::VisitObjCMessageExpr( return; StringRef Comment = - StringRef(Result.getLiteralData(), Result.getLength()).trim("\""); + StringRef(Result.getLiteralData(), Result.getLength()).trim('"'); if ((Comment.trim().size() == 0 && Comment.size() > 0) || // Is Whitespace Comment.empty()) { diff --git a/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp index 4cbe97b2607..c038a2649e1 100644 --- a/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp @@ -75,7 +75,7 @@ void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, // _dispatch_once is then a function which then calls the real dispatch_once. // Users do not care; they just want the warning at the top-level call. if (CE->getLocStart().isMacroID()) { - StringRef TrimmedFName = FName.ltrim("_"); + StringRef TrimmedFName = FName.ltrim('_'); if (TrimmedFName != FName) FName = TrimmedFName; } From bc61800312fbd356852650f9f5b6be030f10ff55 Mon Sep 17 00:00:00 2001 From: Frederic Riss Date: Tue, 16 Feb 2016 20:14:41 -0800 Subject: [PATCH 171/742] Revert "Add -Wfor-loop-analysis to -Wall." This reverts commit 8ca5a7b5297ad1393af858a01af0f46e0f3eb2e0. The -Wfor-loop-analysis has some false positives in Obj-C code. Remove it from -Wall for now until we fix it. --- include/clang/Basic/DiagnosticGroups.td | 1 - 1 file changed, 1 deletion(-) diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td index 2e4e57b63b8..8e5f57d6d89 100644 --- a/include/clang/Basic/DiagnosticGroups.td +++ b/include/clang/Basic/DiagnosticGroups.td @@ -616,7 +616,6 @@ def Most : DiagGroup<"most", [ CharSubscript, Comment, DeleteNonVirtualDtor, - ForLoopAnalysis, Format, Implicit, InfiniteRecursion, From 27bbba17d6a1f0feb309f88540795046fc590c75 Mon Sep 17 00:00:00 2001 From: Mehdi Amini Date: Sun, 14 Feb 2016 22:57:50 -0800 Subject: [PATCH 172/742] Teach clang to use the ThinLTO pipeline Summary: Use the new pipeline implemented in D17115 Reviewers: tejohnson Subscribers: joker.eph, cfe-commits Differential Revision: http://reviews.llvm.org/D17272 --- lib/CodeGen/BackendUtil.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/CodeGen/BackendUtil.cpp b/lib/CodeGen/BackendUtil.cpp index 457af0611b2..843d1b0d371 100644 --- a/lib/CodeGen/BackendUtil.cpp +++ b/lib/CodeGen/BackendUtil.cpp @@ -324,6 +324,7 @@ void EmitAssemblyHelper::CreatePasses(FunctionInfoIndex *FunctionIndex) { PMBuilder.DisableUnitAtATime = !CodeGenOpts.UnitAtATime; PMBuilder.DisableUnrollLoops = !CodeGenOpts.UnrollLoops; PMBuilder.MergeFunctions = CodeGenOpts.MergeFunctions; + PMBuilder.PrepareForThinLTO = CodeGenOpts.EmitFunctionSummary; PMBuilder.PrepareForLTO = CodeGenOpts.PrepareForLTO; PMBuilder.RerollLoops = CodeGenOpts.RerollLoops; @@ -333,7 +334,7 @@ void EmitAssemblyHelper::CreatePasses(FunctionInfoIndex *FunctionIndex) { // pipeline and pass down the in-memory function index. if (FunctionIndex) { PMBuilder.FunctionIndex = FunctionIndex; - PMBuilder.populateLTOPassManager(*MPM); + PMBuilder.populateThinLTOPassManager(*MPM); return; } From 57cda650c2119a2c3f6aab9c988c766e5ec2b494 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Wed, 17 Feb 2016 21:09:50 +0000 Subject: [PATCH 173/742] [CodeGen] Fix an assert in CodeGenFunction::EmitFunctionEpilog The assert is triggered because isObjCRetainableType() is called on the canonicalized return type that has been stripped of the typedefs and attributes attached to it. To fix this assert, this commit gets the original return type from CurCodeDecl or BlockInfo and uses it instead of the canoicalized type. rdar://problem/24470031 Differential Revision: http://reviews.llvm.org/D16914 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261151 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 0add6aa837f9aeb2954009fe61921650b578e50c) --- lib/CodeGen/CGCall.cpp | 20 ++++++++++- .../auto-release-result-assert.mm | 35 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 test/CodeGenObjCXX/auto-release-result-assert.mm diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index 715e8e7aadc..645dc5ae767 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -14,6 +14,7 @@ #include "CGCall.h" #include "ABIInfo.h" +#include "CGBlocks.h" #include "CGCXXABI.h" #include "CGCleanup.h" #include "CodeGenFunction.h" @@ -2462,9 +2463,26 @@ void CodeGenFunction::EmitFunctionEpilog(const CGFunctionInfo &FI, // In ARC, end functions that return a retainable type with a call // to objc_autoreleaseReturnValue. if (AutoreleaseResult) { +#ifndef NDEBUG + // Type::isObjCRetainabletype has to be called on a QualType that hasn't + // been stripped of the typedefs, so we cannot use RetTy here. Get the + // original return type of FunctionDecl, CurCodeDecl, and BlockDecl from + // CurCodeDecl or BlockInfo. + QualType RT; + + if (auto *FD = dyn_cast(CurCodeDecl)) + RT = FD->getReturnType(); + else if (auto *MD = dyn_cast(CurCodeDecl)) + RT = MD->getReturnType(); + else if (isa(CurCodeDecl)) + RT = BlockInfo->BlockExpression->getFunctionType()->getReturnType(); + else + llvm_unreachable("Unexpected function/method type"); + assert(getLangOpts().ObjCAutoRefCount && !FI.isReturnsRetained() && - RetTy->isObjCRetainableType()); + RT->isObjCRetainableType()); +#endif RV = emitAutoreleaseOfResult(*this, RV); } diff --git a/test/CodeGenObjCXX/auto-release-result-assert.mm b/test/CodeGenObjCXX/auto-release-result-assert.mm new file mode 100644 index 00000000000..044dc9d7d99 --- /dev/null +++ b/test/CodeGenObjCXX/auto-release-result-assert.mm @@ -0,0 +1,35 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fblocks -fobjc-arc -o - %s | FileCheck %s + +// CHECK-LABEL: define %struct.S1* @_Z4foo1i( +// CHECK: %[[CALL:[a-z0-9]+]] = call %struct.S1* @_Z4foo0i +// CHECK: ret %struct.S1* %[[CALL]] + +// CHECK-LABEL: define %struct.S1* @_ZN2S22m1Ev( +// CHECK: %[[CALL:[a-z0-9]+]] = call %struct.S1* @_Z4foo0i +// CHECK: ret %struct.S1* %[[CALL]] + +// CHECK-LABEL: define internal %struct.S1* @Block1_block_invoke( +// CHECK: %[[CALL:[a-z0-9]+]] = call %struct.S1* @_Z4foo0i +// CHECK: ret %struct.S1* %[[CALL]] + +struct S1; + +typedef __attribute__((NSObject)) struct __attribute__((objc_bridge(id))) S1 * S1Ref; + +S1Ref foo0(int); + +struct S2 { + S1Ref m1(); +}; + +S1Ref foo1(int a) { + return foo0(a); +} + +S1Ref S2::m1() { + return foo0(0); +} + +S1Ref (^Block1)(void) = ^{ + return foo0(0); +}; From e7782f945876172199850ef20d707ed6e73bee21 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 18 Feb 2016 10:16:29 -0800 Subject: [PATCH 174/742] Fix for compiling with MSVC, from @vitalyster --- include/clang/Basic/SourceMgrAdapter.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/clang/Basic/SourceMgrAdapter.h b/include/clang/Basic/SourceMgrAdapter.h index 6782aebbeff..dd7b83f1a51 100644 --- a/include/clang/Basic/SourceMgrAdapter.h +++ b/include/clang/Basic/SourceMgrAdapter.h @@ -70,7 +70,9 @@ class SourceMgrAdapter { void handleDiag(const llvm::SMDiagnostic &diag); /// Retrieve the diagnostic handler to use with the underlying SourceMgr. - llvm::SourceMgr::DiagHandlerTy getDiagHandler() { return &handleDiag; } + llvm::SourceMgr::DiagHandlerTy getDiagHandler() { + return &SourceMgrAdapter::handleDiag; + } /// Retrieve the context to use with the diagnostic handler produced by /// \c getDiagHandler(). From bc8c4f805e18ef43c591a88b8365cd598192b679 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Thu, 18 Feb 2016 21:05:09 +0000 Subject: [PATCH 175/742] [Sema] Fix bug in TypeLocBuilder::pushImpl The code in TypeLocBuilder::pushImpl wasn't correctly handling the case where an element that has an 8-byte alignment was being pushed. I plan to follow up with a patch to remove redundancies and simplify the function. rdar://problem/23838912 Differential Revision: http://reviews.llvm.org/D16843 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261260 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit b85dafb9b7bee1e51a4f86d8e7e5af054b96b3f8) --- lib/Sema/TypeLocBuilder.cpp | 36 ++++++++++++++++++++--- test/SemaObjCXX/typeloc-data-alignment.mm | 12 ++++++++ 2 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 test/SemaObjCXX/typeloc-data-alignment.mm diff --git a/lib/Sema/TypeLocBuilder.cpp b/lib/Sema/TypeLocBuilder.cpp index be995400df6..340b7fae78a 100644 --- a/lib/Sema/TypeLocBuilder.cpp +++ b/lib/Sema/TypeLocBuilder.cpp @@ -115,11 +115,39 @@ TypeLoc TypeLocBuilder::pushImpl(QualType T, size_t LocalSize, unsigned LocalAli NumBytesAtAlign4 += LocalSize; } } else if (LocalAlignment == 8) { - if (!NumBytesAtAlign8 && NumBytesAtAlign4 % 8 != 0) { - // No existing padding and misaligned members; add in 4 bytes padding - memmove(&Buffer[Index - 4], &Buffer[Index], NumBytesAtAlign4); - Index -= 4; + if (NumBytesAtAlign8 == 0) { + // We have not seen any 8-byte aligned element yet. We insert a padding + // only if the new Index is not 8-byte-aligned. + if ((Index - LocalSize) % 8 != 0) { + memmove(&Buffer[Index - 4], &Buffer[Index], NumBytesAtAlign4); + Index -= 4; + } + } else { + unsigned Padding = NumBytesAtAlign4 % 8; + if (Padding == 0) { + if (LocalSize % 8 == 0) { + // Everything is set: there's no padding and we don't need to add + // any. + } else { + assert(LocalSize % 8 == 4); + // No existing padding; add in 4 bytes padding + memmove(&Buffer[Index - 4], &Buffer[Index], NumBytesAtAlign4); + Index -= 4; + } + } else { + assert(Padding == 4); + if (LocalSize % 8 == 0) { + // Everything is set: there's 4 bytes padding and we don't need + // to add any. + } else { + assert(LocalSize % 8 == 4); + // There are 4 bytes padding, but we don't need any; remove it. + memmove(&Buffer[Index + 4], &Buffer[Index], NumBytesAtAlign4); + Index += 4; + } + } } + // Forget about any padding. NumBytesAtAlign4 = 0; NumBytesAtAlign8 += LocalSize; diff --git a/test/SemaObjCXX/typeloc-data-alignment.mm b/test/SemaObjCXX/typeloc-data-alignment.mm new file mode 100644 index 00000000000..e17a910a607 --- /dev/null +++ b/test/SemaObjCXX/typeloc-data-alignment.mm @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +// expected-no-diagnostics + +// Make sure this doesn't crash. + +@protocol P +@end +template +id

foo(T) { + int i; + foo(i); +} From 24879ad777ee9a8ed1a5d2756197f3449255210f Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 18 Feb 2016 16:35:59 -0800 Subject: [PATCH 176/742] Add -iapinotes-modules search path option. This option adds a new module-centric search path to find the API notes file that applies to the current module that needs to be built. It costs us only 2 stats per search path at module construction time to determine if API notes are available, making it far more efficient (and easier to use) than the prior API notes searching mechanism. It also fits much better with the future direction of the Swift Clang importer, which will soon delegate its responsibilities to Clang's API notes infrastructure. This is part of rdar://problem/24447420. --- include/clang/APINotes/APINotesManager.h | 32 ++- include/clang/APINotes/APINotesOptions.h | 37 ++++ include/clang/APINotes/APINotesReader.h | 2 +- include/clang/Driver/Options.td | 2 + include/clang/Frontend/CompilerInstance.h | 7 + include/clang/Frontend/CompilerInvocation.h | 9 + lib/APINotes/APINotesManager.cpp | 200 +++++++++++------- lib/Driver/Tools.cpp | 6 +- lib/Frontend/CompilerInstance.cpp | 7 + lib/Frontend/CompilerInvocation.cpp | 8 + lib/Sema/Sema.cpp | 3 +- lib/Sema/SemaAPINotes.cpp | 5 +- .../Inputs/APINotes/HeaderLib.apinotes | 17 ++ .../APINotes/Inputs/APINotes/SomeKit.apinotes | 34 +++ .../Modules/module.modulemap | 5 + .../Modules/module.private.modulemap | 8 + .../Modules/module_private.modulemap | 8 + test/APINotes/Inputs/Headers/module.modulemap | 3 + test/APINotes/availability.m | 3 +- test/APINotes/nullability.c | 2 +- test/APINotes/nullability.m | 2 +- test/APINotes/objc_designated_inits.m | 2 +- 22 files changed, 309 insertions(+), 93 deletions(-) create mode 100644 include/clang/APINotes/APINotesOptions.h create mode 100644 test/APINotes/Inputs/APINotes/HeaderLib.apinotes create mode 100644 test/APINotes/Inputs/APINotes/SomeKit.apinotes create mode 100644 test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.modulemap create mode 100644 test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.private.modulemap create mode 100644 test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module_private.modulemap create mode 100644 test/APINotes/Inputs/Headers/module.modulemap diff --git a/include/clang/APINotes/APINotesManager.h b/include/clang/APINotes/APINotesManager.h index 503b36536e6..69752a9c77e 100644 --- a/include/clang/APINotes/APINotesManager.h +++ b/include/clang/APINotes/APINotesManager.h @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// // -// This file defines the HeaderSearch interface. +// This file defines the APINotesManager interface. // //===----------------------------------------------------------------------===// @@ -15,15 +15,18 @@ #define LLVM_CLANG_APINOTES_APINOTESMANAGER_H #include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/StringRef.h" #include +#include namespace clang { class DirectoryEntry; class FileEntry; +class LangOptions; class SourceManager; namespace api_notes { @@ -47,6 +50,13 @@ class APINotesManager { SourceManager &SourceMgr; + /// Whether to implicitly search for API notes files based on the + /// source file from which an entity was declared. + bool ImplicitAPINotes; + + /// The API notes reader for the current module. + std::unique_ptr CurrentModuleReader; + /// Whether we have already pruned the API notes cache. bool PrunedCache; @@ -56,6 +66,13 @@ class APINotesManager { /// reader for this directory. llvm::DenseMap Readers; + /// Load the API notes associated with the given file, whether it is + /// the binary or source form of API notes. + /// + /// \returns the API notes reader for this file, or null if there is + /// a failure. + std::unique_ptr loadAPINotes(const FileEntry *apiNotesFile); + /// Load the given API notes file for the given header directory. /// /// \param HeaderDir The directory at which we @@ -78,9 +95,20 @@ class APINotesManager { bool Public); public: - APINotesManager(SourceManager &SourceMgr); + APINotesManager(SourceManager &sourceMgr, const LangOptions &langOpts); ~APINotesManager(); + /// Load the API notes for the current module. + /// + /// \param moduleName The name of the current module. + /// \param searchPaths The paths in which we should search for API notes + /// for the current module. + /// + /// \returns the file entry for the API notes file loaded, or nullptr if + /// no API notes were found. + const FileEntry *loadCurrentModuleAPINotes(StringRef moduleName, + ArrayRef searchPaths); + /// Find the API notes reader that corresponds to the given source location. APINotesReader *findAPINotes(SourceLocation Loc); }; diff --git a/include/clang/APINotes/APINotesOptions.h b/include/clang/APINotes/APINotesOptions.h new file mode 100644 index 00000000000..01c7513a1d3 --- /dev/null +++ b/include/clang/APINotes/APINotesOptions.h @@ -0,0 +1,37 @@ +//===--- APINotesOptions.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the APINotesOptions class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_APINOTES_APINOTESOPTIONS_H +#define LLVM_CLANG_APINOTES_APINOTESOPTIONS_H + +#include +#include + +namespace clang { + +/// APINotesOptions - Track various options which control how API +/// notes are found and handled. +class APINotesOptions { +public: + /// The set of search paths where we API notes can be found for + /// particular modules. + /// + /// The API notes in this directory are stored as + /// .apinotes or .apinotesc, and are only + /// applied when building the module . + std::vector ModuleSearchPaths; +}; + +} // end namespace clang + +#endif diff --git a/include/clang/APINotes/APINotesReader.h b/include/clang/APINotes/APINotesReader.h index 77b5f16bb42..4759f85c1ab 100644 --- a/include/clang/APINotes/APINotesReader.h +++ b/include/clang/APINotes/APINotesReader.h @@ -38,7 +38,7 @@ class APINotesReader { /// contains the contents of a binary API notes file. /// /// \returns the new API notes reader, or null if an error occurred. - static std::unique_ptr + static std::unique_ptr get(std::unique_ptr inputBuffer); ~APINotesReader(); diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index 55297d823c4..7abe538275d 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -1216,6 +1216,8 @@ def help : Flag<["-", "--"], "help">, Flags<[CC1Option,CC1AsOption]>, HelpText<"Display available options">; def index_header_map : Flag<["-"], "index-header-map">, Flags<[CC1Option]>, HelpText<"Make the next included directory (-I or -F) an indexer header map">; +def iapinotes_modules : JoinedOrSeparate<["-"], "iapinotes-modules">, Group, Flags<[CC1Option]>, + HelpText<"Add directory to the API notes search path referenced by module name">, MetaVarName<"">; def idirafter : JoinedOrSeparate<["-"], "idirafter">, Group, Flags<[CC1Option]>, HelpText<"Add directory to AFTER include search path">; def iframework : JoinedOrSeparate<["-"], "iframework">, Group, Flags<[CC1Option]>, diff --git a/include/clang/Frontend/CompilerInstance.h b/include/clang/Frontend/CompilerInstance.h index 83eed2cdc59..798cc2ddec1 100644 --- a/include/clang/Frontend/CompilerInstance.h +++ b/include/clang/Frontend/CompilerInstance.h @@ -291,6 +291,13 @@ class CompilerInstance : public ModuleLoader { return Invocation->getHeaderSearchOpts(); } + APINotesOptions &getAPINotesOpts() { + return Invocation->getAPINotesOpts(); + } + const APINotesOptions &getAPINotesOpts() const { + return Invocation->getAPINotesOpts(); + } + LangOptions &getLangOpts() { return *Invocation->getLangOpts(); } diff --git a/include/clang/Frontend/CompilerInvocation.h b/include/clang/Frontend/CompilerInvocation.h index 0b4a1e587e7..fa04af27627 100644 --- a/include/clang/Frontend/CompilerInvocation.h +++ b/include/clang/Frontend/CompilerInvocation.h @@ -10,6 +10,7 @@ #ifndef LLVM_CLANG_FRONTEND_COMPILERINVOCATION_H_ #define LLVM_CLANG_FRONTEND_COMPILERINVOCATION_H_ +#include "clang/APINotes/APINotesOptions.h" #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/FileSystemOptions.h" #include "clang/Basic/LangOptions.h" @@ -105,6 +106,9 @@ class CompilerInvocation : public CompilerInvocationBase { MigratorOptions MigratorOpts; + /// Options controlling API notes. + APINotesOptions APINotesOpts; + /// Options controlling IRgen and the backend. CodeGenOptions CodeGenOpts; @@ -173,6 +177,11 @@ class CompilerInvocation : public CompilerInvocationBase { const MigratorOptions &getMigratorOpts() const { return MigratorOpts; } + + APINotesOptions &getAPINotesOpts() { return APINotesOpts; } + const APINotesOptions &getAPINotesOpts() const { + return APINotesOpts; + } CodeGenOptions &getCodeGenOpts() { return CodeGenOpts; } const CodeGenOptions &getCodeGenOpts() const { diff --git a/lib/APINotes/APINotesManager.cpp b/lib/APINotes/APINotesManager.cpp index 50736939a60..3c3d5fdfc41 100644 --- a/lib/APINotes/APINotesManager.cpp +++ b/lib/APINotes/APINotesManager.cpp @@ -12,10 +12,12 @@ //===----------------------------------------------------------------------===// #include "clang/APINotes/APINotesManager.h" +#include "clang/APINotes/APINotesOptions.h" #include "clang/APINotes/APINotesReader.h" #include "clang/APINotes/APINotesYAMLCompiler.h" #include "clang/Basic/DiagnosticIDs.h" #include "clang/Basic/FileManager.h" +#include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/SourceMgrAdapter.h" #include "clang/Basic/Version.h" @@ -49,9 +51,10 @@ STATISTIC(NumBinaryCacheMisses, STATISTIC(NumBinaryCacheRebuilds, "binary form cache rebuilds"); -APINotesManager::APINotesManager(SourceManager &SourceMgr) - : SourceMgr(SourceMgr), PrunedCache(false) { } - +APINotesManager::APINotesManager(SourceManager &sourceMgr, + const LangOptions &langOpts) + : SourceMgr(sourceMgr), ImplicitAPINotes(langOpts.APINotes), + PrunedCache(false) { } APINotesManager::~APINotesManager() { // Free the API notes readers. @@ -131,155 +134,145 @@ static void pruneAPINotesCache(StringRef APINotesCachePath) { } } -bool APINotesManager::loadAPINotes(const DirectoryEntry *HeaderDir, - const FileEntry *APINotesFile) { - assert(Readers.find(HeaderDir) == Readers.end()); - - FileManager &FileMgr = SourceMgr.getFileManager(); +std::unique_ptr +APINotesManager::loadAPINotes(const FileEntry *apiNotesFile) { + FileManager &fileMgr = SourceMgr.getFileManager(); // If the API notes file is already in the binary form, load it directly. - StringRef APINotesFileName = APINotesFile->getName(); - StringRef APINotesFileExt = llvm::sys::path::extension(APINotesFileName); - if (!APINotesFileExt.empty() && - APINotesFileExt.substr(1) == BINARY_APINOTES_EXTENSION) { + StringRef apiNotesFileName = apiNotesFile->getName(); + StringRef apiNotesFileExt = llvm::sys::path::extension(apiNotesFileName); + if (!apiNotesFileExt.empty() && + apiNotesFileExt.substr(1) == BINARY_APINOTES_EXTENSION) { // Load the file. - auto Buffer = FileMgr.getBufferForFile(APINotesFile); - if (!Buffer) { - Readers[HeaderDir] = nullptr; - return true; - } + auto buffer = fileMgr.getBufferForFile(apiNotesFile); + if (!buffer) return nullptr; // Load the binary form. - auto Reader = APINotesReader::get(std::move(Buffer.get())); - if (!Reader) { - Readers[HeaderDir] = nullptr; - return true; - } - - // Record the reader. - Readers[HeaderDir] = Reader.release(); - return false; + return APINotesReader::get(std::move(buffer.get())); } // If we haven't pruned the API notes cache yet during this execution, do // so now. if (!PrunedCache) { - pruneAPINotesCache(FileMgr.getFileSystemOpts().APINotesCachePath); + pruneAPINotesCache(fileMgr.getFileSystemOpts().APINotesCachePath); PrunedCache = true; } // Compute a hash of the API notes file's directory and the Clang version, // to be used as part of the filename for the cached binary copy. - auto code = llvm::hash_value(StringRef(APINotesFile->getDir()->getName())); + auto code = llvm::hash_value(StringRef(apiNotesFile->getDir()->getName())); code = hash_combine(code, getClangFullRepositoryVersion()); // Determine the file name for the cached binary form. - SmallString<128> CompiledFileName; - CompiledFileName += FileMgr.getFileSystemOpts().APINotesCachePath; - assert(!CompiledFileName.empty() && "No API notes cache path provided?"); - llvm::sys::path::append(CompiledFileName, - (llvm::Twine(llvm::sys::path::stem(APINotesFileName)) + "-" + SmallString<128> compiledFileName; + compiledFileName += fileMgr.getFileSystemOpts().APINotesCachePath; + assert(!compiledFileName.empty() && "No API notes cache path provided?"); + llvm::sys::path::append(compiledFileName, + (llvm::Twine(llvm::sys::path::stem(apiNotesFileName)) + "-" + llvm::APInt(64, code).toString(36, /*Signed=*/false) + "." + BINARY_APINOTES_EXTENSION)); // Try to open the cached binary form. - if (const FileEntry *CompiledFile = FileMgr.getFile(CompiledFileName, + if (const FileEntry *compiledFile = fileMgr.getFile(compiledFileName, /*openFile=*/true, /*cacheFailure=*/false)) { // Load the file contents. - if (auto Buffer = FileMgr.getBufferForFile(CompiledFile)) { + if (auto buffer = fileMgr.getBufferForFile(compiledFile)) { // Make sure the file is up-to-date. - if (CompiledFile->getModificationTime() - >= APINotesFile->getModificationTime()) { + if (compiledFile->getModificationTime() + >= apiNotesFile->getModificationTime()) { // Load the file. - if (auto Reader = APINotesReader::get(std::move(Buffer.get()))) { + if (auto reader = APINotesReader::get(std::move(buffer.get()))) { // Success. ++NumBinaryCacheHits; - Readers[HeaderDir] = Reader.release(); - return false; + return reader; } } } // The cache entry was somehow broken; delete this one so we can build a // new one below. - llvm::sys::fs::remove(CompiledFileName.str()); + llvm::sys::fs::remove(compiledFileName.str()); ++NumBinaryCacheRebuilds; } else { ++NumBinaryCacheMisses; } // Open the source file. - auto Buffer = FileMgr.getBufferForFile(APINotesFile); - if (!Buffer) { - Readers[HeaderDir] = nullptr; - return true; - } + auto buffer = fileMgr.getBufferForFile(apiNotesFile); + if (!buffer) return nullptr; // Compile the API notes source into a buffer. // FIXME: Either propagate OSType through or, better yet, improve the binary // APINotes format to maintain complete availability information. - llvm::SmallVector APINotesBuffer; + llvm::SmallVector apiNotesBuffer; { SourceMgrAdapter srcMgrAdapter(SourceMgr, SourceMgr.getDiagnostics(), diag::err_apinotes_message, diag::warn_apinotes_message, diag::note_apinotes_message, - APINotesFile); - llvm::raw_svector_ostream OS(APINotesBuffer); - if (api_notes::compileAPINotes(Buffer.get()->getBuffer(), + apiNotesFile); + llvm::raw_svector_ostream OS(apiNotesBuffer); + if (api_notes::compileAPINotes(buffer.get()->getBuffer(), OS, api_notes::OSType::Absent, srcMgrAdapter.getDiagHandler(), - srcMgrAdapter.getDiagContext())) { - Readers[HeaderDir] = nullptr; - return true; - } + srcMgrAdapter.getDiagContext())) + return nullptr; // Make a copy of the compiled form into the buffer. - Buffer = llvm::MemoryBuffer::getMemBufferCopy( - StringRef(APINotesBuffer.data(), APINotesBuffer.size())); + buffer = llvm::MemoryBuffer::getMemBufferCopy( + StringRef(apiNotesBuffer.data(), apiNotesBuffer.size())); } // Save the binary form into the cache. Perform this operation // atomically. - SmallString<64> TemporaryBinaryFileName = CompiledFileName.str(); - TemporaryBinaryFileName.erase( - TemporaryBinaryFileName.end() - - llvm::sys::path::extension(TemporaryBinaryFileName).size(), - TemporaryBinaryFileName.end()); - TemporaryBinaryFileName += "-%%%%%%."; - TemporaryBinaryFileName += BINARY_APINOTES_EXTENSION; - - int TemporaryFD; + SmallString<64> temporaryBinaryFileName = compiledFileName.str(); + temporaryBinaryFileName.erase( + temporaryBinaryFileName.end() + - llvm::sys::path::extension(temporaryBinaryFileName).size(), + temporaryBinaryFileName.end()); + temporaryBinaryFileName += "-%%%%%%."; + temporaryBinaryFileName += BINARY_APINOTES_EXTENSION; + + int temporaryFD; llvm::sys::fs::create_directories( - FileMgr.getFileSystemOpts().APINotesCachePath); - if (!llvm::sys::fs::createUniqueFile(TemporaryBinaryFileName.str(), - TemporaryFD, TemporaryBinaryFileName)) { + fileMgr.getFileSystemOpts().APINotesCachePath); + if (!llvm::sys::fs::createUniqueFile(temporaryBinaryFileName.str(), + temporaryFD, temporaryBinaryFileName)) { // Write the contents of the buffer. bool hadError; { - llvm::raw_fd_ostream Out(TemporaryFD, /*shouldClose=*/true); - Out.write(Buffer.get()->getBufferStart(), Buffer.get()->getBufferSize()); - Out.flush(); + llvm::raw_fd_ostream out(temporaryFD, /*shouldClose=*/true); + out.write(buffer.get()->getBufferStart(), buffer.get()->getBufferSize()); + out.flush(); - hadError = Out.has_error(); + hadError = out.has_error(); } if (!hadError) { // Rename the temporary file to the actual compiled file. - llvm::sys::fs::rename(TemporaryBinaryFileName.str(), - CompiledFileName.str()); + llvm::sys::fs::rename(temporaryBinaryFileName.str(), + compiledFileName.str()); } } // Load the binary form we just compiled. - auto Reader = APINotesReader::get(std::move(*Buffer)); - assert(Reader && "Could not load the API notes we just generated?"); + auto reader = APINotesReader::get(std::move(*buffer)); + assert(reader && "Could not load the API notes we just generated?"); + return reader; +} - // Record the reader. - Readers[HeaderDir] = Reader.release(); - return false; +bool APINotesManager::loadAPINotes(const DirectoryEntry *HeaderDir, + const FileEntry *APINotesFile) { + assert(Readers.find(HeaderDir) == Readers.end()); + if (auto reader = loadAPINotes(APINotesFile)) { + Readers[HeaderDir] = reader.release(); + return false; + } + + Readers[HeaderDir] = nullptr; + return true; } const DirectoryEntry *APINotesManager::loadFrameworkAPINotes( @@ -332,7 +325,56 @@ const DirectoryEntry *APINotesManager::loadFrameworkAPINotes( return HeaderDir; } +const FileEntry *APINotesManager::loadCurrentModuleAPINotes( + StringRef moduleName, + ArrayRef searchPaths) { + assert(!CurrentModuleReader && + "Already loaded API notes for the current module?"); + + FileManager &fileMgr = SourceMgr.getFileManager(); + + // Look for API notes for this module in the module search paths. + for (const auto &searchPath : searchPaths) { + // First, look for a binary API notes file. + llvm::SmallString<128> apiNotesFilePath; + apiNotesFilePath += searchPath; + llvm::sys::path::append( + apiNotesFilePath, + llvm::Twine(moduleName) + "." + BINARY_APINOTES_EXTENSION); + + // Try to open the binary API Notes file. + if (const FileEntry *binaryAPINotesFile + = fileMgr.getFile(apiNotesFilePath)) { + CurrentModuleReader = loadAPINotes(binaryAPINotesFile); + return CurrentModuleReader ? binaryAPINotesFile : nullptr; + } + + // Try to open the source API Notes file. + apiNotesFilePath = searchPath; + llvm::sys::path::append( + apiNotesFilePath, + llvm::Twine(moduleName) + "." + SOURCE_APINOTES_EXTENSION); + if (const FileEntry *sourceAPINotesFile + = fileMgr.getFile(apiNotesFilePath)) { + CurrentModuleReader = loadAPINotes(sourceAPINotesFile); + return CurrentModuleReader ? sourceAPINotesFile : nullptr; + } + } + + // Didn't find any API notes. + return nullptr; +} + APINotesReader *APINotesManager::findAPINotes(SourceLocation Loc) { + // If there is a reader for the current module, return it. + if (CurrentModuleReader) return CurrentModuleReader.get(); + + // If we're not allowed to implicitly load API notes files, we're done. + if (!ImplicitAPINotes) return nullptr; + + // If we don't have source location information, we're done. + if (Loc.isInvalid()) return nullptr; + // API notes are associated with the expansion location. Retrieve the // file for this location. SourceLocation ExpansionLoc = SourceMgr.getExpansionLoc(Loc); diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index 1d07fa0c045..c9411eb4aac 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -4843,8 +4843,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back("-fno-assume-sane-operator-new"); if (Args.hasFlag(options::OPT_fapinotes, options::OPT_fno_apinotes, - false)) { - CmdArgs.push_back("-fapinotes"); + false) || + Args.hasArg(options::OPT_iapinotes_modules)) { + if (Args.hasFlag(options::OPT_fapinotes, options::OPT_fno_apinotes, false)) + CmdArgs.push_back("-fapinotes"); SmallString<128> APINotesCachePath; if (Arg *A = Args.getLastArg(options::OPT_fapinotes_cache_path)) { diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp index d33b631c0df..acb983af6a3 100644 --- a/lib/Frontend/CompilerInstance.cpp +++ b/lib/Frontend/CompilerInstance.cpp @@ -531,6 +531,13 @@ void CompilerInstance::createSema(TranslationUnitKind TUKind, CodeCompleteConsumer *CompletionConsumer) { TheSema.reset(new Sema(getPreprocessor(), getASTContext(), getASTConsumer(), TUKind, CompletionConsumer)); + + // If we're building a module, notify the API notes manager. + if (!getLangOpts().CurrentModule.empty()) { + (void)TheSema->APINotes.loadCurrentModuleAPINotes( + getLangOpts().CurrentModule, + getAPINotesOpts().ModuleSearchPaths); + } } // Output Files diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index d6d1c792a8f..14f9474f3e2 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1312,6 +1312,12 @@ static void ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args) { Opts.AddVFSOverlayFile(A->getValue()); } +static void ParseAPINotesArgs(APINotesOptions &Opts, ArgList &Args) { + using namespace options; + for (const Arg *A : Args.filtered(OPT_iapinotes_modules)) + Opts.ModuleSearchPaths.push_back(A->getValue()); +} + void CompilerInvocation::setLangDefaults(LangOptions &Opts, InputKind IK, LangStandard::Kind LangStd) { // Set some properties which depend solely on the input kind; it would be nice @@ -2077,6 +2083,8 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, Success &= ParseCodeGenArgs(Res.getCodeGenOpts(), Args, DashX, Diags, Res.getTargetOpts()); ParseHeaderSearchArgs(Res.getHeaderSearchOpts(), Args); + ParseAPINotesArgs(Res.getAPINotesOpts(), Args); + if (DashX == IK_AST || DashX == IK_LLVM_IR) { // ObjCAAutoRefCount and Sanitize LangOpts are used to setup the // PassManager in BackendUtil.cpp. They need to be initializd no matter diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index 34261f2556c..5b93d73c847 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -77,7 +77,8 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, isMultiplexExternalSource(false), FPFeatures(pp.getLangOpts()), LangOpts(pp.getLangOpts()), PP(pp), Context(ctxt), Consumer(consumer), Diags(PP.getDiagnostics()), SourceMgr(PP.getSourceManager()), - APINotes(SourceMgr), CollectStats(false), CodeCompleter(CodeCompleter), + APINotes(SourceMgr, LangOpts), CollectStats(false), + CodeCompleter(CodeCompleter), CurContext(nullptr), OriginalLexicalContext(nullptr), PackContext(nullptr), MSStructPragmaOn(false), MSPointerToMemberRepresentationMethod( diff --git a/lib/Sema/SemaAPINotes.cpp b/lib/Sema/SemaAPINotes.cpp index 3114805da20..7817754c9f2 100644 --- a/lib/Sema/SemaAPINotes.cpp +++ b/lib/Sema/SemaAPINotes.cpp @@ -208,10 +208,7 @@ static void ProcessAPINotes(Sema &S, ObjCContainerDecl *D, /// Process API notes that are associated with this declaration, mapping them /// to attributes as appropriate. void Sema::ProcessAPINotes(Decl *D) { - if (!Context.getLangOpts().APINotes) - return; - - if (!D || D->getLocation().isInvalid()) + if (!D) return; // Globals. diff --git a/test/APINotes/Inputs/APINotes/HeaderLib.apinotes b/test/APINotes/Inputs/APINotes/HeaderLib.apinotes new file mode 100644 index 00000000000..a4ddafe2892 --- /dev/null +++ b/test/APINotes/Inputs/APINotes/HeaderLib.apinotes @@ -0,0 +1,17 @@ +Name: HeaderLib +Functions: + - Name: custom_realloc + NullabilityOfRet: N + Nullability: [ N, S ] + - Name: unavailable_function + Availability: none + AvailabilityMsg: "I beg you not to use this" + - Name: do_something_with_pointers + NullabilityOfRet: O + Nullability: [ N, O ] + +Globals: + - Name: global_int + Nullability: N + - Name: unavailable_global_int + Availability: none diff --git a/test/APINotes/Inputs/APINotes/SomeKit.apinotes b/test/APINotes/Inputs/APINotes/SomeKit.apinotes new file mode 100644 index 00000000000..e7004120126 --- /dev/null +++ b/test/APINotes/Inputs/APINotes/SomeKit.apinotes @@ -0,0 +1,34 @@ +Name: SomeKit +Classes: + - Name: A + Methods: + - Selector: "transform:" + MethodKind: Instance + Availability: none + AvailabilityMsg: "anything but this" + - Selector: "transform:integer:" + MethodKind: Instance + NullabilityOfRet: N + Nullability: [ N, S ] + - Selector: "privateTransform:input:" + MethodKind: Instance + NullabilityOfRet: N + Nullability: [ N, S ] + Properties: + - Name: intValue + Availability: none + AvailabilityMsg: "wouldn't work anyway" + - Name: internalProperty + Nullability: N + - Name: B + Availability: none + AvailabilityMsg: "just don't" + - Name: C + Methods: + - Selector: "initWithA:" + MethodKind: Instance + DesignatedInit: true +Protocols: + - Name: InternalProtocol + Availability: none + AvailabilityMsg: "not for you" diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.modulemap b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.modulemap new file mode 100644 index 00000000000..3abee2df0be --- /dev/null +++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.modulemap @@ -0,0 +1,5 @@ +framework module SomeKit { + umbrella header "SomeKit.h" + export * + module * { export * } +} diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.private.modulemap b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.private.modulemap new file mode 100644 index 00000000000..bbda9d08e39 --- /dev/null +++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.private.modulemap @@ -0,0 +1,8 @@ +module SomeKit.Private { + header "SomeKit_Private.h" + export * + + explicit module NullAnnotation { + header "SomeKit_PrivateForNullAnnotation.h" + } +} diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module_private.modulemap b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module_private.modulemap new file mode 100644 index 00000000000..e31034317cb --- /dev/null +++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module_private.modulemap @@ -0,0 +1,8 @@ +explicit framework module SomeKit.Private { + header "SomeKit_Private.h" + explicit NullAnnotation { header "SomeKit_PrivateForNullAnnotation.h" } + export * + module * { export * } +syntax error + +} diff --git a/test/APINotes/Inputs/Headers/module.modulemap b/test/APINotes/Inputs/Headers/module.modulemap new file mode 100644 index 00000000000..3e59efcf2c4 --- /dev/null +++ b/test/APINotes/Inputs/Headers/module.modulemap @@ -0,0 +1,3 @@ +module HeaderLib { + header "HeaderLib.h" +} diff --git a/test/APINotes/availability.m b/test/APINotes/availability.m index 177700d6311..5b996ec8c94 100644 --- a/test/APINotes/availability.m +++ b/test/APINotes/availability.m @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// RUN: rm -rf %t +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify #include "HeaderLib.h" #import diff --git a/test/APINotes/nullability.c b/test/APINotes/nullability.c index 940587e8e0a..54b0df6cea9 100644 --- a/test/APINotes/nullability.c +++ b/test/APINotes/nullability.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify #include "HeaderLib.h" diff --git a/test/APINotes/nullability.m b/test/APINotes/nullability.m index 40901f2c4fd..ba51cd64d82 100644 --- a/test/APINotes/nullability.m +++ b/test/APINotes/nullability.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify #import diff --git a/test/APINotes/objc_designated_inits.m b/test/APINotes/objc_designated_inits.m index 194d135b6c0..5eb5fa103d5 100644 --- a/test/APINotes/objc_designated_inits.m +++ b/test/APINotes/objc_designated_inits.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify #include "HeaderLib.h" #import From 3adfdd245438361fc8e14de99381bd250fd24876 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 18 Feb 2016 16:36:27 -0800 Subject: [PATCH 177/742] Map FactoryAsInit: C to a hidden attribute. The FactoryAsInit entry in API notes wasn't getting mapped to any Clang attributes. Since there is no use for such an attribute purely in Objective-C, map it to a new unspellable attribute (SwiftSuppressFactoryAsInitAttr) used only to appropriately annotate the declaration for consumption by the Swift Clang importer. Part of rdar://problem/24447420. --- include/clang/Basic/Attr.td | 8 ++++++++ lib/Sema/SemaAPINotes.cpp | 6 ++++++ test/APINotes/Inputs/APINotes/SomeKit.apinotes | 6 ++++++ .../Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h | 4 ++++ 4 files changed, 24 insertions(+) diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index c6126866fed..99be3afccf2 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -1333,6 +1333,14 @@ def SwiftPrivate : InheritableAttr { let Documentation = [Undocumented]; } +def SwiftSuppressFactoryAsInit : InheritableAttr { + // This attribute has no spellings as it is only ever created implicitly + // from API notes. + let Spellings = []; + let SemaHandler = 0; + let Documentation = [Undocumented]; +} + def ReqdWorkGroupSize : InheritableAttr { let Spellings = [GNU<"reqd_work_group_size">]; let Args = [UnsignedArgument<"XDim">, UnsignedArgument<"YDim">, diff --git a/lib/Sema/SemaAPINotes.cpp b/lib/Sema/SemaAPINotes.cpp index 7817754c9f2..8ca364eec19 100644 --- a/lib/Sema/SemaAPINotes.cpp +++ b/lib/Sema/SemaAPINotes.cpp @@ -192,6 +192,12 @@ static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D, } } + if (Info.getFactoryAsInitKind() + == api_notes::FactoryAsInitKind::AsClassMethod && + !D->getAttr()) { + D->addAttr(SwiftSuppressFactoryAsInitAttr::CreateImplicit(S.Context)); + } + // Handle common function information. ProcessAPINotes(S, FunctionOrMethod(D), static_cast(Info)); diff --git a/test/APINotes/Inputs/APINotes/SomeKit.apinotes b/test/APINotes/Inputs/APINotes/SomeKit.apinotes index e7004120126..d251491ed43 100644 --- a/test/APINotes/Inputs/APINotes/SomeKit.apinotes +++ b/test/APINotes/Inputs/APINotes/SomeKit.apinotes @@ -28,6 +28,12 @@ Classes: - Selector: "initWithA:" MethodKind: Instance DesignatedInit: true + - Name: ProcessInfo + Methods: + - Selector: "processInfo" + MethodKind: Class + FactoryAsInit: C + Protocols: - Name: InternalProtocol Availability: none diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h index 01b003d1eee..5e0d7e0b7ab 100644 --- a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h +++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h @@ -20,4 +20,8 @@ __attribute__((objc_root_class)) - (instancetype)initWithA:(A*)a; @end +@interface ProcessInfo : A ++(instancetype)processInfo; +@end + #endif From 3d015ad63e94f179bb9154e8a0905d4cb62ea64c Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 18 Feb 2016 16:39:30 -0800 Subject: [PATCH 178/742] Add "nonswift" availability mapping to Swift unavailability. The new "nonswift" availability maps to __attribute__((swift,unavailable)). Part of rdar://problem/24447420. --- include/clang/APINotes/Types.h | 16 ++++++++++++++-- lib/APINotes/APINotesFormat.h | 2 +- lib/APINotes/APINotesReader.cpp | 4 +++- lib/APINotes/APINotesWriter.cpp | 2 +- lib/APINotes/APINotesYAMLCompiler.cpp | 14 +++++++++++--- lib/Sema/SemaAPINotes.cpp | 10 ++++++++++ 6 files changed, 40 insertions(+), 8 deletions(-) diff --git a/include/clang/APINotes/Types.h b/include/clang/APINotes/Types.h index 8173bf4f3a2..3cf149357bb 100644 --- a/include/clang/APINotes/Types.h +++ b/include/clang/APINotes/Types.h @@ -67,12 +67,16 @@ class CommonEntityInfo { /// Whether this entity is marked unavailable. unsigned Unavailable : 1; - CommonEntityInfo() : Unavailable(0) { } + /// Whether this entity is marked unavailable in Swift. + unsigned UnavailableInSwift : 1; + + CommonEntityInfo() : Unavailable(0), UnavailableInSwift(0) { } friend bool operator==(const CommonEntityInfo &lhs, const CommonEntityInfo &rhs) { return lhs.UnavailableMsg == rhs.UnavailableMsg && - lhs.Unavailable == rhs.Unavailable; + lhs.Unavailable == rhs.Unavailable && + lhs.UnavailableInSwift == rhs.UnavailableInSwift; } friend bool operator!=(const CommonEntityInfo &lhs, @@ -91,6 +95,14 @@ class CommonEntityInfo { } } + if (rhs.UnavailableInSwift) { + lhs.UnavailableInSwift = true; + if (rhs.UnavailableMsg.length() != 0 && + lhs.UnavailableMsg.length() == 0) { + lhs.UnavailableMsg = rhs.UnavailableMsg; + } + } + return lhs; } diff --git a/lib/APINotes/APINotesFormat.h b/lib/APINotes/APINotesFormat.h index 5462e5a5586..0801d2ceefd 100644 --- a/lib/APINotes/APINotesFormat.h +++ b/lib/APINotes/APINotesFormat.h @@ -35,7 +35,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 6; +const uint16_t VERSION_MINOR = 7; using IdentifierID = Fixnum<31>; using IdentifierIDField = BCVBR<16>; diff --git a/lib/APINotes/APINotesReader.cpp b/lib/APINotes/APINotesReader.cpp index a84f2d70491..c768f3a3df7 100644 --- a/lib/APINotes/APINotesReader.cpp +++ b/lib/APINotes/APINotesReader.cpp @@ -30,7 +30,9 @@ using namespace llvm; namespace { /// Read serialized CommonEntityInfo. void readCommonEntityInfo(const uint8_t *&data, CommonEntityInfo &info) { - info.Unavailable = *data++; + uint8_t unavailableBits = *data++; + info.Unavailable = (unavailableBits >> 1) & 0x01; + info.UnavailableInSwift = unavailableBits & 0x01; unsigned msgLength = endian::readNext(data); info.UnavailableMsg diff --git a/lib/APINotes/APINotesWriter.cpp b/lib/APINotes/APINotesWriter.cpp index 400bd08e013..0e498a797a3 100644 --- a/lib/APINotes/APINotesWriter.cpp +++ b/lib/APINotes/APINotesWriter.cpp @@ -272,7 +272,7 @@ namespace { static void emitCommonEntityInfo(raw_ostream &out, const CommonEntityInfo &info) { endian::Writer writer(out); - writer.write(info.Unavailable); + writer.write(info.Unavailable << 1 | info.UnavailableInSwift); writer.write(info.UnavailableMsg.size()); out.write(info.UnavailableMsg.c_str(), info.UnavailableMsg.size()); } diff --git a/lib/APINotes/APINotesYAMLCompiler.cpp b/lib/APINotes/APINotesYAMLCompiler.cpp index 1f299873ce3..8c27a01e5ed 100644 --- a/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/lib/APINotes/APINotesYAMLCompiler.cpp @@ -44,7 +44,7 @@ Availability: OSX # Optional: Specifies which platform the API is # available on. [OSX / iOS / none/ - # available] + # available / nonswift] AvailabilityMsg: "" # Optional: Custom availability message to display to # the user, when API is not available. @@ -143,6 +143,7 @@ namespace { OSX, IOS, None, + NonSwift, }; enum class MethodKind { @@ -264,6 +265,7 @@ namespace llvm { io.enumCase(value, "OSX", APIAvailability::OSX); io.enumCase(value, "iOS", APIAvailability::IOS); io.enumCase(value, "none", APIAvailability::None); + io.enumCase(value, "nonswift", APIAvailability::NonSwift); io.enumCase(value, "available", APIAvailability::Available); } }; @@ -410,9 +412,10 @@ static bool compile(const Module &module, bool convertAvailability(const AvailabilityItem &in, CommonEntityInfo &outInfo, llvm::StringRef apiName) { - // Populate the 'Unavailable' information. + // Populate the unavailability information. outInfo.Unavailable = (in.Mode == APIAvailability::None); - if (outInfo.Unavailable) { + outInfo.UnavailableInSwift = (in.Mode == APIAvailability::NonSwift); + if (outInfo.Unavailable || outInfo.UnavailableInSwift) { outInfo.UnavailableMsg = in.Msg; } else { if (!in.Msg.empty()) { @@ -716,6 +719,11 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, availability.Mode = APIAvailability::None; availability.Msg = copyString(info.UnavailableMsg); } + + if (info.UnavailableInSwift) { + availability.Mode = APIAvailability::NonSwift; + availability.Msg = copyString(info.UnavailableMsg); + } } /// Map nullability information for a function. diff --git a/lib/Sema/SemaAPINotes.cpp b/lib/Sema/SemaAPINotes.cpp index 8ca364eec19..6f08fd4c923 100644 --- a/lib/Sema/SemaAPINotes.cpp +++ b/lib/Sema/SemaAPINotes.cpp @@ -99,6 +99,16 @@ static void ProcessAPINotes(Sema &S, Decl *D, if (Info.Unavailable && !D->hasAttr()) { D->addAttr(UnavailableAttr::CreateImplicit(S.Context, Info.UnavailableMsg)); } + + if (Info.UnavailableInSwift) { + D->addAttr(AvailabilityAttr::CreateImplicit(S.Context, + &S.Context.Idents.get("swift"), + VersionTuple(), + VersionTuple(), + VersionTuple(), + /*Unavailable=*/true, + Info.UnavailableMsg)); + } } /// Process API notes for a variable or property. From fdafe3f0534485bfb68355121eafb7a5d5320ead Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Sat, 6 Feb 2016 00:53:33 +0000 Subject: [PATCH 179/742] [www] Update analyzer website for checker-278. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259967 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 65c5afc8eca9653fb2f69c2349d4c6feaabc2c52) --- www/analyzer/latest_checker.html.incl | 2 +- www/analyzer/release_notes.html | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/www/analyzer/latest_checker.html.incl b/www/analyzer/latest_checker.html.incl index 84d64e61a9a..0f236208dff 100644 --- a/www/analyzer/latest_checker.html.incl +++ b/www/analyzer/latest_checker.html.incl @@ -1 +1 @@ -checker-277.tar.bz2 (built October 28, 2015) +checker-278.tar.bz2 (built February 5, 2016) diff --git a/www/analyzer/release_notes.html b/www/analyzer/release_notes.html index be78a1933c9..29ce46e59af 100644 --- a/www/analyzer/release_notes.html +++ b/www/analyzer/release_notes.html @@ -14,6 +14,21 @@

Release notes for checker-XXX builds

+

checker-278

+

built: February 6, 2016
+ download: checker-278.tar.bz2

+

highlights:

+
    +
  • Greatly improves analysis of C++ lambdas, including interprocedural analysis of lambda applications and reduced 'dead store' + false positives for variables captured by reference.
  • +
  • The analyzer now checks for misuse of 'vfork()'. This check is enabled by default.
  • +
  • The analyzer can now detect excessively-padded structs. This check can be enabled by passing the following + command to scan-build:
    +   -enable-checker optin.performance.Padding
  • +
  • The checks to detect misuse of _Nonnull are now enabled by default.
  • +
  • The checks to detect misuse of Objective-C generics are now enabled by default.
  • +
  • Many miscellaneous improvements.
  • +

checker-277

built: October 28, 2015
From 190b9ecb81d10a90bd5af08be72c59d9a748e4df Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Sat, 6 Feb 2016 00:59:14 +0000 Subject: [PATCH 180/742] [www] Update analyzer release notes to correct the checker-278 build date. This is not the future. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259969 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 208c623c2b99387bc4be8ef86e78b9ec1572d30e) --- www/analyzer/release_notes.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/analyzer/release_notes.html b/www/analyzer/release_notes.html index 29ce46e59af..85aa08f158a 100644 --- a/www/analyzer/release_notes.html +++ b/www/analyzer/release_notes.html @@ -15,7 +15,7 @@

Release notes for checker-XXX builds

checker-278

-

built: February 6, 2016
+

built: February 5, 2016
download: checker-278.tar.bz2

highlights:

    From e5b02d4213e71efd382f69a2ac6597b7abdf1f52 Mon Sep 17 00:00:00 2001 From: Anton Yartsev Date: Wed, 10 Feb 2016 19:46:41 +0000 Subject: [PATCH 181/742] [analyzer] Windows: launch scan-build from an arbitrary location. The following batch files allow to launch scan-build from an arbitrary location if path to clang\tools\scan-build-py\bin is added to %PATH%. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260420 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 918e5c80b606ee3df8e0cfaabb0a1f4a50c48806) --- tools/scan-build-py/bin/analyze-build.bat | 1 + tools/scan-build-py/bin/analyze-c++.bat | 1 + tools/scan-build-py/bin/analyze-cc.bat | 1 + tools/scan-build-py/bin/intercept-build.bat | 1 + tools/scan-build-py/bin/intercept-c++.bat | 1 + tools/scan-build-py/bin/intercept-cc.bat | 1 + tools/scan-build-py/bin/scan-build.bat | 1 + 7 files changed, 7 insertions(+) create mode 100644 tools/scan-build-py/bin/analyze-build.bat create mode 100644 tools/scan-build-py/bin/analyze-c++.bat create mode 100644 tools/scan-build-py/bin/analyze-cc.bat create mode 100644 tools/scan-build-py/bin/intercept-build.bat create mode 100644 tools/scan-build-py/bin/intercept-c++.bat create mode 100644 tools/scan-build-py/bin/intercept-cc.bat create mode 100644 tools/scan-build-py/bin/scan-build.bat diff --git a/tools/scan-build-py/bin/analyze-build.bat b/tools/scan-build-py/bin/analyze-build.bat new file mode 100644 index 00000000000..05d81ddfda4 --- /dev/null +++ b/tools/scan-build-py/bin/analyze-build.bat @@ -0,0 +1 @@ +python %~dp0analyze-build %* diff --git a/tools/scan-build-py/bin/analyze-c++.bat b/tools/scan-build-py/bin/analyze-c++.bat new file mode 100644 index 00000000000..f57032f60bd --- /dev/null +++ b/tools/scan-build-py/bin/analyze-c++.bat @@ -0,0 +1 @@ +python %~dp0analyze-c++ %* diff --git a/tools/scan-build-py/bin/analyze-cc.bat b/tools/scan-build-py/bin/analyze-cc.bat new file mode 100644 index 00000000000..41cd8f622eb --- /dev/null +++ b/tools/scan-build-py/bin/analyze-cc.bat @@ -0,0 +1 @@ +python %~dp0analyze-cc %* diff --git a/tools/scan-build-py/bin/intercept-build.bat b/tools/scan-build-py/bin/intercept-build.bat new file mode 100644 index 00000000000..5c824635dfe --- /dev/null +++ b/tools/scan-build-py/bin/intercept-build.bat @@ -0,0 +1 @@ +python %~dp0intercept-build %* diff --git a/tools/scan-build-py/bin/intercept-c++.bat b/tools/scan-build-py/bin/intercept-c++.bat new file mode 100644 index 00000000000..abbd4b177e0 --- /dev/null +++ b/tools/scan-build-py/bin/intercept-c++.bat @@ -0,0 +1 @@ +python %~dp0intercept-c++ %* diff --git a/tools/scan-build-py/bin/intercept-cc.bat b/tools/scan-build-py/bin/intercept-cc.bat new file mode 100644 index 00000000000..23cbd8d22ca --- /dev/null +++ b/tools/scan-build-py/bin/intercept-cc.bat @@ -0,0 +1 @@ +python %~dp0intercept-cc %* diff --git a/tools/scan-build-py/bin/scan-build.bat b/tools/scan-build-py/bin/scan-build.bat new file mode 100644 index 00000000000..8caf240a2f0 --- /dev/null +++ b/tools/scan-build-py/bin/scan-build.bat @@ -0,0 +1 @@ +python %~dp0scan-build %* From de0af3a4fd6616e61ef73346ac81647f1c373cf1 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Thu, 11 Feb 2016 22:13:20 +0000 Subject: [PATCH 182/742] [analyzer] Improve pattern matching in ObjCDealloc checker. Look through PseudoObjectExpr and OpaqueValueExprs when scanning for release-like operations. This commit also adds additional tests in anticipation of re-writing this as a path-sensitive checker. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260608 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 6df7834ea1564dc1602d8d98a7c9390d17e8bf25) --- .../Checkers/CheckObjCDealloc.cpp | 25 +++++--- test/Analysis/DeallocMissingRelease.m | 60 +++++++++++++++++++ test/Analysis/PR2978.m | 9 +++ 3 files changed, 86 insertions(+), 8 deletions(-) diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index 1a3519307a2..902babfe502 100644 --- a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -28,6 +28,16 @@ using namespace clang; using namespace ento; +// FIXME: This was taken from IvarInvalidationChecker.cpp +static const Expr *peel(const Expr *E) { + E = E->IgnoreParenCasts(); + if (const PseudoObjectExpr *POE = dyn_cast(E)) + E = POE->getSyntacticForm()->IgnoreParenCasts(); + if (const OpaqueValueExpr *OVE = dyn_cast(E)) + E = OVE->getSourceExpr()->IgnoreParenCasts(); + return E; +} + static bool scan_ivar_release(Stmt *S, const ObjCIvarDecl *ID, const ObjCPropertyDecl *PD, Selector Release, @@ -38,30 +48,29 @@ static bool scan_ivar_release(Stmt *S, const ObjCIvarDecl *ID, if (ObjCMessageExpr *ME = dyn_cast(S)) if (ME->getSelector() == Release) if (ME->getInstanceReceiver()) - if (Expr *Receiver = ME->getInstanceReceiver()->IgnoreParenCasts()) - if (ObjCIvarRefExpr *E = dyn_cast(Receiver)) + if (const Expr *Receiver = peel(ME->getInstanceReceiver())) + if (auto *E = dyn_cast(Receiver)) if (E->getDecl() == ID) return true; // [self setMyIvar:nil]; if (ObjCMessageExpr *ME = dyn_cast(S)) if (ME->getInstanceReceiver()) - if (Expr *Receiver = ME->getInstanceReceiver()->IgnoreParenCasts()) - if (DeclRefExpr *E = dyn_cast(Receiver)) + if (const Expr *Receiver = peel(ME->getInstanceReceiver())) + if (auto *E = dyn_cast(Receiver)) if (E->getDecl()->getIdentifier() == SelfII) if (ME->getMethodDecl() == PD->getSetterMethodDecl() && ME->getNumArgs() == 1 && - ME->getArg(0)->isNullPointerConstant(Ctx, + peel(ME->getArg(0))->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNull)) return true; // self.myIvar = nil; if (BinaryOperator* BO = dyn_cast(S)) if (BO->isAssignmentOp()) - if (ObjCPropertyRefExpr *PRE = - dyn_cast(BO->getLHS()->IgnoreParenCasts())) + if (auto *PRE = dyn_cast(peel(BO->getLHS()))) if (PRE->isExplicitProperty() && PRE->getExplicitProperty() == PD) - if (BO->getRHS()->isNullPointerConstant(Ctx, + if (peel(BO->getRHS())->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNull)) { // This is only a 'release' if the property kind is not // 'assign'. diff --git a/test/Analysis/DeallocMissingRelease.m b/test/Analysis/DeallocMissingRelease.m index 3a2b556c11d..e782f9968f9 100644 --- a/test/Analysis/DeallocMissingRelease.m +++ b/test/Analysis/DeallocMissingRelease.m @@ -189,4 +189,64 @@ - (void)dealloc { #endif } @end + +@interface ClassWithControlFlowInRelease : NSObject { + BOOL _ivar1; +} +@property (retain) NSObject *ivar2; +@end + +@implementation ClassWithControlFlowInRelease +- (void)dealloc; { + if (_ivar1) { + // We really should warn because there is a path through -dealloc on which + // _ivar2 is not released. +#if NON_ARC + [_ivar2 release]; // no-warning +#endif + } + +#if NON_ARC + [super dealloc]; +#endif +} + +@end + +//===------------------------------------------------------------------------=== +// Don't warn when the property is nil'd out in -dealloc + +@interface ClassWithNildOutProperty : NSObject +@property (retain) NSObject *ivar; // no-warning +@end + +@implementation ClassWithNildOutProperty +- (void)dealloc; { + self.ivar = nil; + +#if NON_ARC + [super dealloc]; +#endif +} + +@end + +//===------------------------------------------------------------------------=== +// Don't warn when the property is nil'd out with a setter in -dealloc + +@interface ClassWithNildOutPropertyViaSetter : NSObject +@property (retain) NSObject *ivar; // no-warning +@end + +@implementation ClassWithNildOutPropertyViaSetter +- (void)dealloc; { + [self setIvar:nil]; + +#if NON_ARC + [super dealloc]; +#endif +} + +@end + // CHECK: 4 warnings generated. diff --git a/test/Analysis/PR2978.m b/test/Analysis/PR2978.m index 1fbd9723a2b..2067b3e85af 100644 --- a/test/Analysis/PR2978.m +++ b/test/Analysis/PR2978.m @@ -17,6 +17,8 @@ @interface MyClass : NSObject { id _L; id _N; id _M; + id _P; + id _Q; id _V; id _W; } @@ -27,6 +29,9 @@ @interface MyClass : NSObject { @property(weak) id L; @property(readonly) id N; @property(retain) id M; +@property(weak) id P; // expected-warning {{'_P' instance variable in 'MyClass' was not retained by a synthesized property but was released in 'dealloc'}} +@property(weak) id Q; + @property(retain) id V; @property(retain) id W; -(id) O; @@ -41,6 +46,7 @@ @implementation MyClass @synthesize L = _L; // no-warning @synthesize N = _N; // no-warning @synthesize M = _M; +@synthesize Q = _Q; // expected-warning {{'_Q' instance variable in 'MyClass' was not retained by a synthesized property but was released in 'dealloc'}} @synthesize V = _V; @synthesize W = _W; // expected-warning{{The '_W' instance variable in 'MyClass' was retained by a synthesized property but was not released in 'dealloc'}} @@ -57,6 +63,9 @@ - (id)dealloc [self setV:0]; // This will release '_V' [self setW:@"newW"]; // This will release '_W', but retain the new value self.O = 0; // no-warning + + [_Q release]; + self.P = 0; [super dealloc]; return 0; } From b87556abdec842c258692624a803658c2034af3c Mon Sep 17 00:00:00 2001 From: Yury Gribov Date: Thu, 18 Feb 2016 11:08:46 +0000 Subject: [PATCH 183/742] [analyzer] Add --force-analyze-debug-code option to scan-build to force debug build and hopefully enable more precise warnings. Static Analyzer is much more efficient when built in debug mode (-UNDEBUG) so we advice users to enable it manually. This may be inconvenient in case of large complex projects (think about Linux distros e.g. Android or Tizen). This patch adds a flag to scan-build which inserts -UNDEBUG automatically. Differential Revision: http://reviews.llvm.org/D16200 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261204 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 19b977bcdf62a9f6fca2c115fd85d2508f4a818b) --- tools/scan-build-py/libscanbuild/analyze.py | 16 +++++++++++++-- tools/scan-build-py/libscanbuild/runner.py | 11 ++++++++-- tools/scan-build-py/tests/unit/test_runner.py | 11 ++++++++++ tools/scan-build/bin/scan-build | 20 ++++++++++++++++--- tools/scan-build/libexec/ccc-analyzer | 8 ++++++++ www/analyzer/scan-build.html | 3 +++ 6 files changed, 62 insertions(+), 7 deletions(-) diff --git a/tools/scan-build-py/libscanbuild/analyze.py b/tools/scan-build-py/libscanbuild/analyze.py index 0d3547befee..9b00d04fc0f 100644 --- a/tools/scan-build-py/libscanbuild/analyze.py +++ b/tools/scan-build-py/libscanbuild/analyze.py @@ -106,7 +106,8 @@ def exclude(filename): 'output_dir': output_dir, 'output_format': args.output_format, 'output_failures': args.output_failures, - 'direct_args': analyzer_params(args) + 'direct_args': analyzer_params(args), + 'force_analyze_debug_code' : args.force_analyze_debug_code } logging.debug('run analyzer against compilation database') @@ -138,7 +139,9 @@ def setup_environment(args, destination, bin_dir): 'ANALYZE_BUILD_REPORT_DIR': destination, 'ANALYZE_BUILD_REPORT_FORMAT': args.output_format, 'ANALYZE_BUILD_REPORT_FAILURES': 'yes' if args.output_failures else '', - 'ANALYZE_BUILD_PARAMETERS': ' '.join(analyzer_params(args)) + 'ANALYZE_BUILD_PARAMETERS': ' '.join(analyzer_params(args)), + 'ANALYZE_BUILD_FORCE_ANALYZE_DEBUG_CODE' + : 'yes' if args.force_analyze_debug_code else '' }) return environment @@ -168,6 +171,8 @@ def analyze_build_wrapper(cplusplus): 'output_failures': os.getenv('ANALYZE_BUILD_REPORT_FAILURES'), 'direct_args': os.getenv('ANALYZE_BUILD_PARAMETERS', '').split(' '), + 'force_analyze_debug_code': + os.getenv('ANALYZE_BUILD_FORCE_ANALYZE_DEBUG_CODE'), 'directory': os.getcwd(), } # get relevant parameters from command line arguments @@ -450,6 +455,13 @@ def create_parser(from_build_command): Could be usefull when project contains 3rd party libraries. The directory path shall be absolute path as file names in the compilation database.""") + advanced.add_argument( + '--force-analyze-debug-code', + dest='force_analyze_debug_code', + action='store_true', + help="""Tells analyzer to enable assertions in code even if they were + disabled during compilation, enabling more precise + results.""") plugins = parser.add_argument_group('checker options') plugins.add_argument( diff --git a/tools/scan-build-py/libscanbuild/runner.py b/tools/scan-build-py/libscanbuild/runner.py index 248ca90ad3e..63b9f743699 100644 --- a/tools/scan-build-py/libscanbuild/runner.py +++ b/tools/scan-build-py/libscanbuild/runner.py @@ -41,6 +41,7 @@ def wrapper(*args, **kwargs): @require(['command', 'directory', 'file', # an entry from compilation database 'clang', 'direct_args', # compiler name, and arguments from command + 'force_analyze_debug_code', # preprocessing options 'output_dir', 'output_format', 'output_failures']) def run(opts): """ Entry point to run (or not) static analyzer against a single entry @@ -164,9 +165,13 @@ def set_analyzer_output(opts, continuation=run_analyzer): opts.update({'output': ['-o', opts['output_dir']]}) return continuation(opts) +def force_analyze_debug_code(cmd): + """ Enable assert()'s by undefining NDEBUG. """ + cmd.append('-UNDEBUG') -@require(['file', 'directory', 'clang', 'direct_args', 'language', - 'output_dir', 'output_format', 'output_failures']) +@require(['file', 'directory', 'clang', 'direct_args', + 'force_analyze_debug_code', 'language', 'output_dir', + 'output_format', 'output_failures']) def create_commands(opts, continuation=set_analyzer_output): """ Create command to run analyzer or failure report generation. @@ -178,6 +183,8 @@ def create_commands(opts, continuation=set_analyzer_output): if 'arch' in opts: common.extend(['-arch', opts.pop('arch')]) common.extend(opts.pop('compile_options', [])) + if opts['force_analyze_debug_code']: + force_analyze_debug_code(common) common.extend(['-x', opts['language']]) common.append(os.path.relpath(opts['file'], opts['directory'])) diff --git a/tools/scan-build-py/tests/unit/test_runner.py b/tools/scan-build-py/tests/unit/test_runner.py index ea10051d850..de15d236920 100644 --- a/tools/scan-build-py/tests/unit/test_runner.py +++ b/tools/scan-build-py/tests/unit/test_runner.py @@ -211,3 +211,14 @@ def test_method_with_expecteds(self): def test_method_exception_not_caught(self): self.assertRaises(Exception, method_exception_from_inside, dict()) + +class ForceAnalyzeDebugTest(unittest.TestCase): + + def test_force_analyze_debug_code(self): + for a, b in [ + ([], ['-UNDEBUG']), + (['-O2'], ['-O2', '-UNDEBUG']), + (['-Dkey=val'], ['-Dkey=val', '-UNDEBUG']), + (['-D', 'NDEBUG'], ['-D', 'NDEBUG', '-UNDEBUG']) ]: + sut.force_analyze_debug_code(a) + self.assertEqual(a, b) diff --git a/tools/scan-build/bin/scan-build b/tools/scan-build/bin/scan-build index 6a14484970a..3182a29767b 100755 --- a/tools/scan-build/bin/scan-build +++ b/tools/scan-build/bin/scan-build @@ -69,7 +69,8 @@ my %Options = ( MaxLoop => 0, PluginsToLoad => [], AnalyzerDiscoveryMethod => undef, - OverrideCompiler => 0 # The flag corresponding to the --override-compiler command line option. + OverrideCompiler => 0, # The flag corresponding to the --override-compiler command line option. + ForceAnalyzeDebugCode => 0 ); lock_keys(%Options); @@ -951,7 +952,8 @@ sub SetEnv { 'CCC_CC', 'CCC_CXX', 'CCC_REPORT_FAILURES', - 'CLANG_ANALYZER_TARGET') { + 'CLANG_ANALYZER_TARGET', + 'CCC_ANALYZER_FORCE_ANALYZE_DEBUG_CODE') { my $x = $EnvVars->{$var}; if (defined $x) { $ENV{$var} = $x } } @@ -1118,6 +1120,11 @@ OPTIONS: Also analyze functions in #included files. By default, such functions are skipped unless they are called by functions within the main source file. + --force-analyze-debug-code + + Tells analyzer to enable assertions in code even if they were disabled + during compilation to enable more precise results. + -o Specifies the output directory for analyzer reports. Subdirectories will be @@ -1681,6 +1688,12 @@ sub ProcessArgs { next; } + if ($arg eq "--force-analyze-debug-code") { + shift @$Args; + $Options{ForceAnalyzeDebugCode} = 1; + next; + } + DieDiag("unrecognized option '$arg'\n") if ($arg =~ /^-/); $NumArgs--; @@ -1796,7 +1809,8 @@ my %EnvVars = ( 'CCC_ANALYZER_CONSTRAINTS_MODEL' => $Options{ConstraintsModel}, 'CCC_ANALYZER_INTERNAL_STATS' => $Options{InternalStats}, 'CCC_ANALYZER_OUTPUT_FORMAT' => $Options{OutputFormat}, - 'CLANG_ANALYZER_TARGET' => $Options{AnalyzerTarget} + 'CLANG_ANALYZER_TARGET' => $Options{AnalyzerTarget}, + 'CCC_ANALYZER_FORCE_ANALYZE_DEBUG_CODE' => $Options{ForceAnalyzeDebugCode} ); # Run the build. diff --git a/tools/scan-build/libexec/ccc-analyzer b/tools/scan-build/libexec/ccc-analyzer index 831dd42e9c9..bfda1d326f9 100755 --- a/tools/scan-build/libexec/ccc-analyzer +++ b/tools/scan-build/libexec/ccc-analyzer @@ -492,6 +492,9 @@ if (defined $ENV{'CCC_ANALYZER_LOG'}) { $Verbose = 2; } # Get the HTML output directory. my $HtmlDir = $ENV{'CCC_ANALYZER_HTML'}; +# Get force-analyze-debug-code option. +my $ForceAnalyzeDebugCode = $ENV{'CCC_ANALYZER_FORCE_ANALYZE_DEBUG_CODE'}; + my %DisabledArchs = ('ppc' => 1, 'ppc64' => 1); my %ArchsSeen; my $HadArch = 0; @@ -682,6 +685,11 @@ foreach (my $i = 0; $i < scalar(@ARGV); ++$i) { } } +# Forcedly enable debugging if requested by user. +if ($ForceAnalyzeDebugCode) { + push @CompileOpts, '-UNDEBUG'; +} + # If we are on OSX and have an installation where the # default SDK is inferred by xcrun use xcrun to infer # the SDK. diff --git a/www/analyzer/scan-build.html b/www/analyzer/scan-build.html index 04e93232a6b..b16f6bb53fa 100644 --- a/www/analyzer/scan-build.html +++ b/www/analyzer/scan-build.html @@ -226,6 +226,9 @@

    Use verbose output when debugging scan-build

    scan-build takes a -v option to emit verbose output about From ce21a1a9573082ea8bebbf1c639cc0cba216089a Mon Sep 17 00:00:00 2001 From: Yury Gribov Date: Thu, 18 Feb 2016 15:43:56 +0000 Subject: [PATCH 184/742] [analyzer] dump_ast_matchers.py: fix replacement regexps Patch by Alex Sidorin! Differential Revision: http://reviews.llvm.org/D17376 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261219 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 98c4d57826376988fa2dbd8eb294ee57e32f0dcd) --- docs/tools/dump_ast_matchers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/tools/dump_ast_matchers.py b/docs/tools/dump_ast_matchers.py index 9ecff049c96..22b09e2b859 100644 --- a/docs/tools/dump_ast_matchers.py +++ b/docs/tools/dump_ast_matchers.py @@ -358,11 +358,11 @@ def sort_table(matcher_type, matcher_map): reference = open('../LibASTMatchersReference.html').read() reference = re.sub(r'', - '%s', reference, flags=re.S) % node_matcher_table + node_matcher_table, reference, flags=re.S) reference = re.sub(r'', - '%s', reference, flags=re.S) % narrowing_matcher_table + narrowing_matcher_table, reference, flags=re.S) reference = re.sub(r'', - '%s', reference, flags=re.S) % traversal_matcher_table + traversal_matcher_table, reference, flags=re.S) with open('../LibASTMatchersReference.html', 'wb') as output: output.write(reference) From 97b8fd3e397fd388267e3a3d073824f050c39ba1 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Thu, 18 Feb 2016 19:13:30 +0000 Subject: [PATCH 185/742] [analyzer] Improve modeling of ObjC synthesized property setters. When modeling a call to a setter for a property that is synthesized to be backed by an instance variable, don't invalidate the entire instance but rather only the storage for the updated instance variable itself. This still doesn't model the effect of the setter completely. It doesn't bind the set value to the ivar storage location because doing so would cause the set value to escape, removing valuable diagnostics about potential leaks of the value from the retain count checker. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261243 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 29e6c0aba58b2b40551cc9416483a44581bcfcfe) --- .../Core/PathSensitive/CallEvent.h | 5 ++ lib/StaticAnalyzer/Core/CallEvent.cpp | 63 ++++++++++++++--- test/Analysis/properties.m | 69 +++++++++++++++++++ 3 files changed, 128 insertions(+), 9 deletions(-) diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h index 55fd4b8880b..7e6a3b9faaf 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -957,6 +957,11 @@ class ObjCMethodCall : public CallEvent { llvm_unreachable("Unknown message kind"); } + // Returns the property accessed by this method, either explicitly via + // property syntax or implicitly via a getter or setter method. Returns + // nullptr if the call is not a prooperty access. + const ObjCPropertyDecl *getAccessedProperty() const; + RuntimeDefinition getRuntimeDefinition() const override; bool argumentsMayEscape() const override; diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp index 59b90b5ce98..2c7b5302dd6 100644 --- a/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -678,9 +678,26 @@ ArrayRef ObjCMethodCall::parameters() const { return D->parameters(); } -void -ObjCMethodCall::getExtraInvalidatedValues(ValueList &Values, - RegionAndSymbolInvalidationTraits *ETraits) const { +void ObjCMethodCall::getExtraInvalidatedValues( + ValueList &Values, RegionAndSymbolInvalidationTraits *ETraits) const { + + // If the method call is a setter for property known to be backed by + // an instance variable, don't invalidate the entire receiver, just + // the storage for that instance variable. + if (const ObjCPropertyDecl *PropDecl = getAccessedProperty()) { + if (const ObjCIvarDecl *PropIvar = PropDecl->getPropertyIvarDecl()) { + SVal IvarLVal = getState()->getLValue(PropIvar, getReceiverSVal()); + const MemRegion *IvarRegion = IvarLVal.getAsRegion(); + ETraits->setTrait( + IvarRegion, + RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion); + ETraits->setTrait(IvarRegion, + RegionAndSymbolInvalidationTraits::TK_SuppressEscape); + Values.push_back(IvarLVal); + return; + } + } + Values.push_back(getReceiverSVal()); } @@ -740,6 +757,18 @@ const PseudoObjectExpr *ObjCMethodCall::getContainingPseudoObjectExpr() const { return ObjCMessageDataTy::getFromOpaqueValue(Data).getPointer(); } +static const Expr * +getSyntacticFromForPseudoObjectExpr(const PseudoObjectExpr *POE) { + const Expr *Syntactic = POE->getSyntacticForm(); + + // This handles the funny case of assigning to the result of a getter. + // This can happen if the getter returns a non-const reference. + if (const BinaryOperator *BO = dyn_cast(Syntactic)) + Syntactic = BO->getLHS(); + + return Syntactic; +} + ObjCMessageKind ObjCMethodCall::getMessageKind() const { if (!Data) { @@ -749,12 +778,7 @@ ObjCMessageKind ObjCMethodCall::getMessageKind() const { // Check if parent is a PseudoObjectExpr. if (const PseudoObjectExpr *POE = dyn_cast_or_null(S)) { - const Expr *Syntactic = POE->getSyntacticForm(); - - // This handles the funny case of assigning to the result of a getter. - // This can happen if the getter returns a non-const reference. - if (const BinaryOperator *BO = dyn_cast(Syntactic)) - Syntactic = BO->getLHS(); + const Expr *Syntactic = getSyntacticFromForPseudoObjectExpr(POE); ObjCMessageKind K; switch (Syntactic->getStmtClass()) { @@ -790,6 +814,27 @@ ObjCMessageKind ObjCMethodCall::getMessageKind() const { return static_cast(Info.getInt()); } +const ObjCPropertyDecl *ObjCMethodCall::getAccessedProperty() const { + // Look for properties accessed with property syntax (foo.bar = ...) + if ( getMessageKind() == OCM_PropertyAccess) { + const PseudoObjectExpr *POE = getContainingPseudoObjectExpr(); + assert(POE && "Property access without PseudoObjectExpr?"); + + const Expr *Syntactic = getSyntacticFromForPseudoObjectExpr(POE); + auto *RefExpr = cast(Syntactic); + + if (RefExpr->isExplicitProperty()) + return RefExpr->getExplicitProperty(); + } + + // Look for properties accessed with method syntax ([foo setBar:...]). + const ObjCMethodDecl *MD = getDecl(); + if (!MD || !MD->isPropertyAccessor()) + return nullptr; + + // Note: This is potentially quite slow. + return MD->findPropertyDecl(); +} bool ObjCMethodCall::canBeOverridenInSubclass(ObjCInterfaceDecl *IDecl, Selector Sel) const { diff --git a/test/Analysis/properties.m b/test/Analysis/properties.m index 4fdbb69d87a..d92f1a1a7e8 100644 --- a/test/Analysis/properties.m +++ b/test/Analysis/properties.m @@ -238,6 +238,75 @@ - (void)testSynthesisForShadowedReadWriteProperties; { } @end +//------ +// Setter ivar invalidation. +//------ + +@interface ClassWithSetters +// Note: These properties have implicit @synthesize implementations to be +// backed with ivars. +@property (assign) int propWithIvar1; +@property (assign) int propWithIvar2; + +@property (retain) NSNumber *retainedProperty; + +@end + +@interface ClassWithSetters (InOtherTranslationUnit) +// The implementation of this property is in another translation unit. +// We don't know whether it is backed by an ivar or not. +@property (assign) int propInOther; +@end + +@implementation ClassWithSetters +- (void) testSettingPropWithIvarInvalidatesExactlyThatIvar; { + _propWithIvar1 = 1; + _propWithIvar2 = 2; + self.propWithIvar1 = 66; + + // Calling the setter of a property backed by the instance variable + // should invalidate the storage for the instance variable but not + // the rest of the receiver. Ideally we would model the setter completely + // but doing so would cause the new value to escape when it is bound + // to the ivar. This would cause bad false negatives in the retain count + // checker. (There is a test for this scenario in + // testWriteRetainedValueToRetainedProperty below). + clang_analyzer_eval(_propWithIvar1 == 66); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(_propWithIvar2 == 2); // expected-warning{{TRUE}} + + _propWithIvar1 = 1; + [self setPropWithIvar1:66]; + + clang_analyzer_eval(_propWithIvar1 == 66); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(_propWithIvar2 == 2); // expected-warning{{TRUE}} +} + +- (void) testSettingPropWithoutIvarInvalidatesEntireInstance; { + _propWithIvar1 = 1; + _propWithIvar2 = 2; + self.propInOther = 66; + + clang_analyzer_eval(_propWithIvar1 == 66); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(_propWithIvar2 == 2); // expected-warning{{UNKNOWN}} + + _propWithIvar1 = 1; + _propWithIvar2 = 2; + [self setPropInOther:66]; + + clang_analyzer_eval(_propWithIvar1 == 66); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(_propWithIvar2 == 2); // expected-warning{{UNKNOWN}} +} + +#if !__has_feature(objc_arc) +- (void) testWriteRetainedValueToRetainedProperty; { + NSNumber *number = [[NSNumber alloc] initWithInteger:5]; // expected-warning {{Potential leak of an object stored into 'number'}} + + // Make sure we catch this leak. + self.retainedProperty = number; +} +#endif +@end + #if !__has_feature(objc_arc) void testOverrelease(Person *p, int coin) { switch (coin) { From 5f0d23fbf705236f7b4538ae1a2c0be6825d5586 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Thu, 18 Feb 2016 19:37:39 +0000 Subject: [PATCH 186/742] [analyzer] Include comment mistakenly left out of r261243. NFC. It explains why we can't just synthesize bodies of setters in BodyFarm. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261248 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit ee128546797a12899c7eae86c27dfdbd061daaa6) --- lib/Analysis/BodyFarm.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/Analysis/BodyFarm.cpp b/lib/Analysis/BodyFarm.cpp index 2118301362e..d8b652f68e8 100644 --- a/lib/Analysis/BodyFarm.cpp +++ b/lib/Analysis/BodyFarm.cpp @@ -498,6 +498,14 @@ Stmt *BodyFarm::getBody(const ObjCMethodDecl *D) { return nullptr; // For now, we only synthesize getters. + // Synthesizing setters would cause false negatives in the + // RetainCountChecker because the method body would bind the parameter + // to an instance variable, causing it to escape. This would prevent + // warning in the following common scenario: + // + // id foo = [[NSObject alloc] init]; + // self.foo = foo; // We should warn that foo leaks here. + // if (D->param_size() != 0) return nullptr; From f4526c3490aa3218de12e53cb80ebb6fa2abd0e3 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Fri, 19 Feb 2016 01:35:10 +0000 Subject: [PATCH 187/742] [analyzer] Add checker callback for beginning of function. Add a checker callback that is called when the analyzer starts analyzing a function either at the top level or when inlined. This will be used by a follow-on patch making the DeallocChecker path sensitive. Differential Revision: http://reviews.llvm.org/D17418 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261293 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 674d89e44b74f5ef119aa3c852d0f237daf4648f) --- include/clang/Analysis/ProgramPoint.h | 7 ++++ include/clang/StaticAnalyzer/Core/Checker.h | 14 +++++++ .../StaticAnalyzer/Core/CheckerManager.h | 13 ++++++- .../Core/PathSensitive/CoreEngine.h | 3 ++ .../Core/PathSensitive/ExprEngine.h | 13 +++++-- .../Core/PathSensitive/SubEngine.h | 14 +++++-- .../Checkers/CheckerDocumentation.cpp | 10 ++++- .../Checkers/TraversalChecker.cpp | 6 +++ lib/StaticAnalyzer/Core/CheckerManager.cpp | 38 +++++++++++++++++++ lib/StaticAnalyzer/Core/CoreEngine.cpp | 24 +++++++++--- lib/StaticAnalyzer/Core/ExprEngine.cpp | 9 +++++ .../Core/ExprEngineCallAndReturn.cpp | 17 +++++---- test/Analysis/traversal-begin-end-function.c | 22 +++++++++++ test/Analysis/traversal-path-unification.c | 1 + 14 files changed, 170 insertions(+), 21 deletions(-) create mode 100644 test/Analysis/traversal-begin-end-function.c diff --git a/include/clang/Analysis/ProgramPoint.h b/include/clang/Analysis/ProgramPoint.h index 6d816fd733e..5045194ef86 100644 --- a/include/clang/Analysis/ProgramPoint.h +++ b/include/clang/Analysis/ProgramPoint.h @@ -595,6 +595,13 @@ class CallEnter : public ProgramPoint { return static_cast(getData2()); } + /// Returns the entry block in the CFG for the entered function. + const CFGBlock *getEntry() const { + const StackFrameContext *CalleeCtx = getCalleeContext(); + const CFG *CalleeCFG = CalleeCtx->getCFG(); + return &(CalleeCFG->getEntry()); + } + private: friend class ProgramPoint; CallEnter() {} diff --git a/include/clang/StaticAnalyzer/Core/Checker.h b/include/clang/StaticAnalyzer/Core/Checker.h index 1410af15681..137f238cced 100644 --- a/include/clang/StaticAnalyzer/Core/Checker.h +++ b/include/clang/StaticAnalyzer/Core/Checker.h @@ -238,6 +238,20 @@ class EndAnalysis { } }; +class BeginFunction { + template + static void _checkBeginFunction(void *checker, CheckerContext &C) { + ((const CHECKER *)checker)->checkBeginFunction(C); + } + +public: + template + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForBeginFunction(CheckerManager::CheckBeginFunctionFunc( + checker, _checkBeginFunction)); + } +}; + class EndFunction { template static void _checkEndFunction(void *checker, diff --git a/include/clang/StaticAnalyzer/Core/CheckerManager.h b/include/clang/StaticAnalyzer/Core/CheckerManager.h index bc9af496b05..612e1056a79 100644 --- a/include/clang/StaticAnalyzer/Core/CheckerManager.h +++ b/include/clang/StaticAnalyzer/Core/CheckerManager.h @@ -287,6 +287,12 @@ class CheckerManager { void runCheckersForEndAnalysis(ExplodedGraph &G, BugReporter &BR, ExprEngine &Eng); + /// \brief Run checkers on begining of function. + void runCheckersForBeginFunction(ExplodedNodeSet &Dst, + const BlockEdge &L, + ExplodedNode *Pred, + ExprEngine &Eng); + /// \brief Run checkers on end of function. void runCheckersForEndFunction(NodeBuilderContext &BC, ExplodedNodeSet &Dst, @@ -425,7 +431,10 @@ class CheckerManager { typedef CheckerFn CheckEndAnalysisFunc; - + + typedef CheckerFn + CheckBeginFunctionFunc; + typedef CheckerFn CheckEndFunctionFunc; @@ -484,6 +493,7 @@ class CheckerManager { void _registerForEndAnalysis(CheckEndAnalysisFunc checkfn); + void _registerForBeginFunction(CheckEndFunctionFunc checkfn); void _registerForEndFunction(CheckEndFunctionFunc checkfn); void _registerForBranchCondition(CheckBranchConditionFunc checkfn); @@ -593,6 +603,7 @@ class CheckerManager { std::vector EndAnalysisCheckers; + std::vector BeginFunctionCheckers; std::vector EndFunctionCheckers; std::vector BranchConditionCheckers; diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h index d5822e22448..0fa736d76c9 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h @@ -91,6 +91,9 @@ class CoreEngine { void HandleBlockEdge(const BlockEdge &E, ExplodedNode *Pred); void HandleBlockEntrance(const BlockEntrance &E, ExplodedNode *Pred); void HandleBlockExit(const CFGBlock *B, ExplodedNode *Pred); + + void HandleCallEnter(const CallEnter &CE, ExplodedNode *Pred); + void HandlePostStmt(const CFGBlock *B, unsigned StmtIdx, ExplodedNode *Pred); void HandleBranch(const Stmt *Cond, const Stmt *Term, const CFGBlock *B, diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index 99083c9d613..ec96560a2f0 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -253,8 +253,14 @@ class ExprEngine : public SubEngine { /// nodes by processing the 'effects' of a switch statement. void processSwitch(SwitchNodeBuilder& builder) override; - /// Called by CoreEngine. Used to generate end-of-path - /// nodes when the control reaches the end of a function. + /// Called by CoreEngine. Used to notify checkers that processing a + /// function has begun. Called for both inlined and and top-level functions. + void processBeginOfFunction(NodeBuilderContext &BC, + ExplodedNode *Pred, ExplodedNodeSet &Dst, + const BlockEdge &L) override; + + /// Called by CoreEngine. Used to notify checkers that processing a + /// function has ended. Called for both inlined and and top-level functions. void processEndOfFunction(NodeBuilderContext& BC, ExplodedNode *Pred) override; @@ -264,7 +270,8 @@ class ExprEngine : public SubEngine { ExplodedNodeSet &Dst); /// Generate the entry node of the callee. - void processCallEnter(CallEnter CE, ExplodedNode *Pred) override; + void processCallEnter(NodeBuilderContext& BC, CallEnter CE, + ExplodedNode *Pred) override; /// Generate the sequence of nodes that simulate the call exit and the post /// visit for CallExpr. diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h index 741ba0e2f29..1a731cf865e 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h @@ -99,13 +99,21 @@ class SubEngine { /// nodes by processing the 'effects' of a switch statement. virtual void processSwitch(SwitchNodeBuilder& builder) = 0; - /// Called by CoreEngine. Used to generate end-of-path - /// nodes when the control reaches the end of a function. + /// Called by CoreEngine. Used to notify checkers that processing a + /// function has begun. Called for both inlined and and top-level functions. + virtual void processBeginOfFunction(NodeBuilderContext &BC, + ExplodedNode *Pred, + ExplodedNodeSet &Dst, + const BlockEdge &L) = 0; + + /// Called by CoreEngine. Used to notify checkers that processing a + /// function has ended. Called for both inlined and and top-level functions. virtual void processEndOfFunction(NodeBuilderContext& BC, ExplodedNode *Pred) = 0; // Generate the entry node of the callee. - virtual void processCallEnter(CallEnter CE, ExplodedNode *Pred) = 0; + virtual void processCallEnter(NodeBuilderContext& BC, CallEnter CE, + ExplodedNode *Pred) = 0; // Generate the first post callsite node. virtual void processCallExit(ExplodedNode *Pred) = 0; diff --git a/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp b/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp index 37b84480f89..efa8139d8fa 100644 --- a/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp @@ -164,8 +164,16 @@ class CheckerDocumentation : public Checker< check::PreStmt, /// check::DeadSymbols void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const {} + + /// \brief Called when the analyzer core starts analyzing a function, + /// regardless of whether it is analyzed at the top level or is inlined. + /// + /// check::BeginFunction + void checkBeginFunction(CheckerContext &Ctx) const {} + /// \brief Called when the analyzer core reaches the end of a - /// function being analyzed. + /// function being analyzed regardless of whether it is analyzed at the top + /// level or is inlined. /// /// check::EndFunction void checkEndFunction(CheckerContext &Ctx) const {} diff --git a/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp b/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp index d02d2df1c50..8ad962875b0 100644 --- a/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp @@ -25,9 +25,11 @@ using namespace ento; namespace { class TraversalDumper : public Checker< check::BranchCondition, + check::BeginFunction, check::EndFunction > { public: void checkBranchCondition(const Stmt *Condition, CheckerContext &C) const; + void checkBeginFunction(CheckerContext &C) const; void checkEndFunction(CheckerContext &C) const; }; } @@ -50,6 +52,10 @@ void TraversalDumper::checkBranchCondition(const Stmt *Condition, << Parent->getStmtClassName() << "\n"; } +void TraversalDumper::checkBeginFunction(CheckerContext &C) const { + llvm::outs() << "--BEGIN FUNCTION--\n"; +} + void TraversalDumper::checkEndFunction(CheckerContext &C) const { llvm::outs() << "--END FUNCTION--\n"; } diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp index 008e8ef31cd..d8382e88691 100644 --- a/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -377,6 +377,40 @@ void CheckerManager::runCheckersForEndAnalysis(ExplodedGraph &G, EndAnalysisCheckers[i](G, BR, Eng); } +namespace { +struct CheckBeginFunctionContext { + typedef std::vector CheckersTy; + const CheckersTy &Checkers; + ExprEngine &Eng; + const ProgramPoint &PP; + + CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } + CheckersTy::const_iterator checkers_end() { return Checkers.end(); } + + CheckBeginFunctionContext(const CheckersTy &Checkers, ExprEngine &Eng, + const ProgramPoint &PP) + : Checkers(Checkers), Eng(Eng), PP(PP) {} + + void runChecker(CheckerManager::CheckBeginFunctionFunc checkFn, + NodeBuilder &Bldr, ExplodedNode *Pred) { + const ProgramPoint &L = PP.withTag(checkFn.Checker); + CheckerContext C(Bldr, Eng, Pred, L); + + checkFn(C); + } +}; +} + +void CheckerManager::runCheckersForBeginFunction(ExplodedNodeSet &Dst, + const BlockEdge &L, + ExplodedNode *Pred, + ExprEngine &Eng) { + ExplodedNodeSet Src; + Src.insert(Pred); + CheckBeginFunctionContext C(BeginFunctionCheckers, Eng, L); + expandGraphWithCheckers(C, Dst, Src); +} + /// \brief Run checkers for end of path. // Note, We do not chain the checker output (like in expandGraphWithCheckers) // for this callback since end of path nodes are expected to be final. @@ -671,6 +705,10 @@ void CheckerManager::_registerForEndAnalysis(CheckEndAnalysisFunc checkfn) { EndAnalysisCheckers.push_back(checkfn); } +void CheckerManager::_registerForBeginFunction(CheckBeginFunctionFunc checkfn) { + BeginFunctionCheckers.push_back(checkfn); +} + void CheckerManager::_registerForEndFunction(CheckEndFunctionFunc checkfn) { EndFunctionCheckers.push_back(checkfn); } diff --git a/lib/StaticAnalyzer/Core/CoreEngine.cpp b/lib/StaticAnalyzer/Core/CoreEngine.cpp index 39cf7e77175..c75fb2e763d 100644 --- a/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ b/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -192,10 +192,18 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, WList->setBlockCounter(BCounterFactory.GetEmptyCounter()); if (!InitState) - // Generate the root. - generateNode(StartLoc, SubEng.getInitialState(L), nullptr); - else - generateNode(StartLoc, InitState, nullptr); + InitState = SubEng.getInitialState(L); + + bool IsNew; + ExplodedNode *Node = G.getNode(StartLoc, InitState, false, &IsNew); + assert (IsNew); + G.addRoot(Node); + + NodeBuilderContext BuilderCtx(*this, StartLoc.getDst(), Node); + ExplodedNodeSet DstBegin; + SubEng.processBeginOfFunction(BuilderCtx, Node, DstBegin, StartLoc); + + enqueue(DstBegin); } // Check if we have a steps limit @@ -243,8 +251,7 @@ void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc, break; case ProgramPoint::CallEnterKind: { - CallEnter CEnter = Loc.castAs(); - SubEng.processCallEnter(CEnter, Pred); + HandleCallEnter(Loc.castAs(), Pred); break; } @@ -456,6 +463,11 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) { Pred->State, Pred); } +void CoreEngine::HandleCallEnter(const CallEnter &CE, ExplodedNode *Pred) { + NodeBuilderContext BuilderCtx(*this, CE.getEntry(), Pred); + SubEng.processCallEnter(BuilderCtx, CE, Pred); +} + void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term, const CFGBlock * B, ExplodedNode *Pred) { assert(B->succ_size() == 2); diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index 662b0a2dd79..2d20183bf4d 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -30,6 +30,7 @@ #include "llvm/ADT/ImmutableList.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Support/SaveAndRestore.h" #ifndef NDEBUG #include "llvm/Support/GraphWriter.h" @@ -1745,6 +1746,14 @@ static bool stackFrameDoesNotContainInitializedTemporaries(ExplodedNode &Pred) { } #endif +void ExprEngine::processBeginOfFunction(NodeBuilderContext &BC, + ExplodedNode *Pred, + ExplodedNodeSet &Dst, + const BlockEdge &L) { + SaveAndRestore NodeContextRAII(currBldrCtx, &BC); + getCheckerManager().runCheckersForBeginFunction(Dst, L, Pred, *this); +} + /// ProcessEndPath - Called by CoreEngine. Used to generate end-of-path /// nodes when the control reaches the end of a function. void ExprEngine::processEndOfFunction(NodeBuilderContext& BC, diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index 2c24dc1353a..3b504bb3c08 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -37,13 +37,12 @@ STATISTIC(NumInlinedCalls, STATISTIC(NumReachedInlineCountMax, "The # of times we reached inline count maximum"); -void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) { +void ExprEngine::processCallEnter(NodeBuilderContext& BC, CallEnter CE, + ExplodedNode *Pred) { // Get the entry block in the CFG of the callee. const StackFrameContext *calleeCtx = CE.getCalleeContext(); PrettyStackTraceLocationContext CrashInfo(calleeCtx); - - const CFG *CalleeCFG = calleeCtx->getCFG(); - const CFGBlock *Entry = &(CalleeCFG->getEntry()); + const CFGBlock *Entry = CE.getEntry(); // Validate the CFG. assert(Entry->empty()); @@ -57,12 +56,16 @@ void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) { ProgramStateRef state = Pred->getState(); - // Construct a new node and add it to the worklist. + // Construct a new node, notify checkers that analysis of the function has + // begun, and add the resultant nodes to the worklist. bool isNew; ExplodedNode *Node = G.getNode(Loc, state, false, &isNew); Node->addPredecessor(Pred, G); - if (isNew) - Engine.getWorkList()->enqueue(Node); + if (isNew) { + ExplodedNodeSet DstBegin; + processBeginOfFunction(BC, Node, DstBegin, Loc); + Engine.enqueue(DstBegin); + } } // Find the last statement on the path to the exploded node and the diff --git a/test/Analysis/traversal-begin-end-function.c b/test/Analysis/traversal-begin-end-function.c new file mode 100644 index 00000000000..810ce1d2a52 --- /dev/null +++ b/test/Analysis/traversal-begin-end-function.c @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.DumpTraversal %s | FileCheck %s + +void inline_callee(int i); + +// CHECK: --BEGIN FUNCTION-- +void inline_caller() { + // CHECK: --BEGIN FUNCTION-- + // CHECK: --BEGIN FUNCTION-- + // CHECK: --BEGIN FUNCTION-- + inline_callee(3); + // CHECK: --END FUNCTION-- + // CHECK: --END FUNCTION-- + // CHECK: --END FUNCTION-- +} +// CHECK: --END FUNCTION-- + +void inline_callee(int i) { + if (i <= 1) + return; + + inline_callee(i - 1); +} diff --git a/test/Analysis/traversal-path-unification.c b/test/Analysis/traversal-path-unification.c index 83e3b87c2bb..3bf6df731c2 100644 --- a/test/Analysis/traversal-path-unification.c +++ b/test/Analysis/traversal-path-unification.c @@ -11,6 +11,7 @@ int c(); #define CHECK(x) (x) #endif +// CHECK: --BEGIN FUNCTION-- void testRemoveDeadBindings() { int i = a(); if (CHECK(i)) From 7486ccb1484120f6201137d7724fc8a711db991a Mon Sep 17 00:00:00 2001 From: Yury Gribov Date: Wed, 3 Feb 2016 13:36:31 +0000 Subject: [PATCH 188/742] Forgot to remove file in previous commit. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259647 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit c3f4599d32c0d6ece9c7c635c1b319f3214ecea3) --- test/Analysis/analyze_display_progress.c | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 test/Analysis/analyze_display_progress.c diff --git a/test/Analysis/analyze_display_progress.c b/test/Analysis/analyze_display_progress.c deleted file mode 100644 index 958ed009ff1..00000000000 --- a/test/Analysis/analyze_display_progress.c +++ /dev/null @@ -1,9 +0,0 @@ -// RUN: %clang_cc1 -analyze -analyzer-display-progress %s 2>&1 | FileCheck %s - -void f() {}; -void g() {}; -void h() {} - -// CHECK: analyze_display_progress.c f -// CHECK: analyze_display_progress.c g -// CHECK: analyze_display_progress.c h \ No newline at end of file From 7fe3f44d5e0593ece2e80dc7e1b3658c3fab646e Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Fri, 29 Jan 2016 13:53:26 +0000 Subject: [PATCH 189/742] Removing unnecessary casts; NFC. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259194 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 8f258397c5afd7a708bd95770c718e81d08fb11a) --- lib/StaticAnalyzer/Core/MemRegion.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp index e1cb2ad5ff0..84c11984e18 100644 --- a/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -1406,9 +1406,9 @@ void BlockDataRegion::LazyInitializeReferencedVars() { BumpVectorContext BC(A); typedef BumpVector VarVec; - VarVec *BV = (VarVec*) A.Allocate(); + VarVec *BV = A.Allocate(); new (BV) VarVec(BC, NumBlockVars); - VarVec *BVOriginal = (VarVec*) A.Allocate(); + VarVec *BVOriginal = A.Allocate(); new (BVOriginal) VarVec(BC, NumBlockVars); for (const VarDecl *VD : ReferencedBlockVars) { From 56f2a99e6c8548a13575f1de29bf9854e96a5551 Mon Sep 17 00:00:00 2001 From: Craig Topper Date: Sun, 31 Jan 2016 00:20:24 +0000 Subject: [PATCH 190/742] Convert an unsigned to Twine instead of using utostr since we're already building a Twine. NFC git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259309 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit d5237ff5017086441656901bf5a45e2f3fefec12) --- lib/StaticAnalyzer/Core/IssueHash.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/StaticAnalyzer/Core/IssueHash.cpp b/lib/StaticAnalyzer/Core/IssueHash.cpp index 0a3af3dcc7e..c352f12111f 100644 --- a/lib/StaticAnalyzer/Core/IssueHash.cpp +++ b/lib/StaticAnalyzer/Core/IssueHash.cpp @@ -180,7 +180,7 @@ std::string clang::GetIssueString(const SourceManager &SM, return (llvm::Twine(CheckerName) + Delimiter + GetEnclosingDeclContextSignature(D) + Delimiter + - llvm::utostr(IssueLoc.getExpansionColumnNumber()) + Delimiter + + Twine(IssueLoc.getExpansionColumnNumber()) + Delimiter + NormalizeLine(SM, IssueLoc, LangOpts) + Delimiter + BugType) .str(); } From b9b2e8940056238a304982e7dba1579dce4a85b1 Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Wed, 3 Feb 2016 15:20:51 +0000 Subject: [PATCH 191/742] Minor cleanup to remove casts and improve some const correctness. NFC. Patch by Alexander Riccio. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259652 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit d35ba45a58c209f9aad9deff4c80e6335cee4c67) --- .../Core/PathSensitive/MemRegion.h | 4 +- lib/StaticAnalyzer/Core/MemRegion.cpp | 52 +++++++++---------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h b/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h index 41935aef0a3..1d33301cf2f 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h @@ -1320,8 +1320,8 @@ class RegionAndSymbolInvalidationTraits { void setTrait(SymbolRef Sym, InvalidationKinds IK); void setTrait(const MemRegion *MR, InvalidationKinds IK); - bool hasTrait(SymbolRef Sym, InvalidationKinds IK); - bool hasTrait(const MemRegion *MR, InvalidationKinds IK); + bool hasTrait(SymbolRef Sym, InvalidationKinds IK) const; + bool hasTrait(const MemRegion *MR, InvalidationKinds IK) const; }; } // end GR namespace diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp index 84c11984e18..11dfb9d9740 100644 --- a/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -46,7 +46,7 @@ RegionTy* MemRegionManager::getRegion(const A1 a1) { InsertPos)); if (!R) { - R = (RegionTy*) A.Allocate(); + R = A.Allocate(); new (R) RegionTy(a1, superRegion); Regions.InsertNode(R, InsertPos); } @@ -64,7 +64,7 @@ RegionTy* MemRegionManager::getSubRegion(const A1 a1, InsertPos)); if (!R) { - R = (RegionTy*) A.Allocate(); + R = A.Allocate(); new (R) RegionTy(a1, superRegion); Regions.InsertNode(R, InsertPos); } @@ -85,7 +85,7 @@ RegionTy* MemRegionManager::getRegion(const A1 a1, const A2 a2) { InsertPos)); if (!R) { - R = (RegionTy*) A.Allocate(); + R = A.Allocate(); new (R) RegionTy(a1, a2, superRegion); Regions.InsertNode(R, InsertPos); } @@ -104,7 +104,7 @@ RegionTy* MemRegionManager::getSubRegion(const A1 a1, const A2 a2, InsertPos)); if (!R) { - R = (RegionTy*) A.Allocate(); + R = A.Allocate(); new (R) RegionTy(a1, a2, superRegion); Regions.InsertNode(R, InsertPos); } @@ -123,7 +123,7 @@ RegionTy* MemRegionManager::getSubRegion(const A1 a1, const A2 a2, const A3 a3, InsertPos)); if (!R) { - R = (RegionTy*) A.Allocate(); + R = A.Allocate(); new (R) RegionTy(a1, a2, a3, superRegion); Regions.InsertNode(R, InsertPos); } @@ -246,23 +246,23 @@ QualType CXXBaseObjectRegion::getValueType() const { //===----------------------------------------------------------------------===// void MemSpaceRegion::Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddInteger((unsigned)getKind()); + ID.AddInteger(static_cast(getKind())); } void StackSpaceRegion::Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddInteger((unsigned)getKind()); + ID.AddInteger(static_cast(getKind())); ID.AddPointer(getStackFrame()); } void StaticGlobalSpaceRegion::Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddInteger((unsigned)getKind()); + ID.AddInteger(static_cast(getKind())); ID.AddPointer(getCodeRegion()); } void StringRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const StringLiteral* Str, const MemRegion* superRegion) { - ID.AddInteger((unsigned) StringRegionKind); + ID.AddInteger(static_cast(StringRegionKind)); ID.AddPointer(Str); ID.AddPointer(superRegion); } @@ -270,7 +270,7 @@ void StringRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, void ObjCStringRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const ObjCStringLiteral* Str, const MemRegion* superRegion) { - ID.AddInteger((unsigned) ObjCStringRegionKind); + ID.AddInteger(static_cast(ObjCStringRegionKind)); ID.AddPointer(Str); ID.AddPointer(superRegion); } @@ -278,7 +278,7 @@ void ObjCStringRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, void AllocaRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const Expr *Ex, unsigned cnt, const MemRegion *superRegion) { - ID.AddInteger((unsigned) AllocaRegionKind); + ID.AddInteger(static_cast(AllocaRegionKind)); ID.AddPointer(Ex); ID.AddInteger(cnt); ID.AddPointer(superRegion); @@ -295,7 +295,7 @@ void CompoundLiteralRegion::Profile(llvm::FoldingSetNodeID& ID) const { void CompoundLiteralRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const CompoundLiteralExpr *CL, const MemRegion* superRegion) { - ID.AddInteger((unsigned) CompoundLiteralRegionKind); + ID.AddInteger(static_cast(CompoundLiteralRegionKind)); ID.AddPointer(CL); ID.AddPointer(superRegion); } @@ -303,7 +303,7 @@ void CompoundLiteralRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, void CXXThisRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, const PointerType *PT, const MemRegion *sRegion) { - ID.AddInteger((unsigned) CXXThisRegionKind); + ID.AddInteger(static_cast(CXXThisRegionKind)); ID.AddPointer(PT); ID.AddPointer(sRegion); } @@ -320,7 +320,7 @@ void ObjCIvarRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, void DeclRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const Decl *D, const MemRegion* superRegion, Kind k) { - ID.AddInteger((unsigned) k); + ID.AddInteger(static_cast(k)); ID.AddPointer(D); ID.AddPointer(superRegion); } @@ -335,7 +335,7 @@ void VarRegion::Profile(llvm::FoldingSetNodeID &ID) const { void SymbolicRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, SymbolRef sym, const MemRegion *sreg) { - ID.AddInteger((unsigned) MemRegion::SymbolicRegionKind); + ID.AddInteger(static_cast(MemRegion::SymbolicRegionKind)); ID.Add(sym); ID.AddPointer(sreg); } @@ -454,7 +454,7 @@ void MemRegion::dumpToStream(raw_ostream &os) const { } void AllocaRegion::dumpToStream(raw_ostream &os) const { - os << "alloca{" << (const void*) Ex << ',' << Cnt << '}'; + os << "alloca{" << static_cast(Ex) << ',' << Cnt << '}'; } void FunctionCodeRegion::dumpToStream(raw_ostream &os) const { @@ -462,7 +462,7 @@ void FunctionCodeRegion::dumpToStream(raw_ostream &os) const { } void BlockCodeRegion::dumpToStream(raw_ostream &os) const { - os << "block_code{" << (const void*) this << '}'; + os << "block_code{" << static_cast(this) << '}'; } void BlockDataRegion::dumpToStream(raw_ostream &os) const { @@ -478,12 +478,12 @@ void BlockDataRegion::dumpToStream(raw_ostream &os) const { void CompoundLiteralRegion::dumpToStream(raw_ostream &os) const { // FIXME: More elaborate pretty-printing. - os << "{ " << (const void*) CL << " }"; + os << "{ " << static_cast(CL) << " }"; } void CXXTempObjectRegion::dumpToStream(raw_ostream &os) const { os << "temp_object{" << getValueType().getAsString() << ',' - << (const void*) Ex << '}'; + << static_cast(Ex) << '}'; } void CXXBaseObjectRegion::dumpToStream(raw_ostream &os) const { @@ -646,7 +646,7 @@ void CXXBaseObjectRegion::printPrettyAsExpr(raw_ostream &os) const { template const REG *MemRegionManager::LazyAllocate(REG*& region) { if (!region) { - region = (REG*) A.Allocate(); + region = A.Allocate(); new (region) REG(this); } @@ -656,7 +656,7 @@ const REG *MemRegionManager::LazyAllocate(REG*& region) { template const REG *MemRegionManager::LazyAllocate(REG*& region, ARG a) { if (!region) { - region = (REG*) A.Allocate(); + region = A.Allocate(); new (region) REG(this, a); } @@ -921,7 +921,7 @@ MemRegionManager::getElementRegion(QualType elementType, NonLoc Idx, ElementRegion* R = cast_or_null(data); if (!R) { - R = (ElementRegion*) A.Allocate(); + R = A.Allocate(); new (R) ElementRegion(T, Idx, superRegion); Regions.InsertNode(R, InsertPos); } @@ -1342,10 +1342,10 @@ RegionOffset MemRegion::getAsOffset() const { // Get the field number. unsigned idx = 0; for (RecordDecl::field_iterator FI = RD->field_begin(), - FE = RD->field_end(); FI != FE; ++FI, ++idx) + FE = RD->field_end(); FI != FE; ++FI, ++idx) { if (FR->getDecl() == *FI) break; - + } const ASTRecordLayout &Layout = getContext().getASTRecordLayout(RD); // This is offset in bits. Offset += Layout.getFieldOffset(idx); @@ -1488,7 +1488,7 @@ void RegionAndSymbolInvalidationTraits::setTrait(const MemRegion *MR, } bool RegionAndSymbolInvalidationTraits::hasTrait(SymbolRef Sym, - InvalidationKinds IK) { + InvalidationKinds IK) const { const_symbol_iterator I = SymTraitsMap.find(Sym); if (I != SymTraitsMap.end()) return I->second & IK; @@ -1497,7 +1497,7 @@ bool RegionAndSymbolInvalidationTraits::hasTrait(SymbolRef Sym, } bool RegionAndSymbolInvalidationTraits::hasTrait(const MemRegion *MR, - InvalidationKinds IK) { + InvalidationKinds IK) const { if (!MR) return false; From 3b2ac424acda115ea11e7f674b0b1ee7e4e12547 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Sat, 20 Feb 2016 20:34:55 +0000 Subject: [PATCH 192/742] [c-index-test] CMake: When installing c-index-test to a different prefix directory, add an rpath so that it can find libclang. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261445 91177308-0d34-0410-b5e6-96231b3b80d8 --- tools/c-index-test/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/c-index-test/CMakeLists.txt b/tools/c-index-test/CMakeLists.txt index 3958e95a90f..e0df5031d3e 100644 --- a/tools/c-index-test/CMakeLists.txt +++ b/tools/c-index-test/CMakeLists.txt @@ -42,6 +42,8 @@ endif() if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) if(INTERNAL_INSTALL_PREFIX) set(INSTALL_DESTINATION "${INTERNAL_INSTALL_PREFIX}/bin") + set_property(TARGET c-index-test APPEND PROPERTY INSTALL_RPATH + "@executable_path/../../lib") else() set(INSTALL_DESTINATION bin) endif() From 11493b0f62e5eb00c29b7b0d6adfbde156d0b388 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Sat, 20 Feb 2016 21:35:48 -0800 Subject: [PATCH 193/742] [API Notes] Add support for SwiftName on anything, which maps to the swift_name attribute. This allows API notes to be used to override the Swift names of Objective-C entities. --- include/clang/APINotes/Types.h | 12 +++++++-- lib/APINotes/APINotesFormat.h | 2 +- lib/APINotes/APINotesReader.cpp | 7 ++++++ lib/APINotes/APINotesWriter.cpp | 4 ++- lib/APINotes/APINotesYAMLCompiler.cpp | 21 ++++++++++++++++ lib/Sema/SemaAPINotes.cpp | 33 +++++++++++++++++++------ test/APINotes/Inputs/roundtrip.apinotes | 13 ++++++++++ 7 files changed, 80 insertions(+), 12 deletions(-) diff --git a/include/clang/APINotes/Types.h b/include/clang/APINotes/Types.h index 3cf149357bb..79f34ccc218 100644 --- a/include/clang/APINotes/Types.h +++ b/include/clang/APINotes/Types.h @@ -58,7 +58,7 @@ class ContextID { /// Describes API notes data for any entity. /// -/// This is used as the base of +/// This is used as the base of all API notes. class CommonEntityInfo { public: /// Message to use when this entity is unavailable. @@ -70,13 +70,17 @@ class CommonEntityInfo { /// Whether this entity is marked unavailable in Swift. unsigned UnavailableInSwift : 1; + /// Swift name of this entity. + std::string SwiftName; + CommonEntityInfo() : Unavailable(0), UnavailableInSwift(0) { } friend bool operator==(const CommonEntityInfo &lhs, const CommonEntityInfo &rhs) { return lhs.UnavailableMsg == rhs.UnavailableMsg && lhs.Unavailable == rhs.Unavailable && - lhs.UnavailableInSwift == rhs.UnavailableInSwift; + lhs.UnavailableInSwift == rhs.UnavailableInSwift && + lhs.SwiftName == rhs.SwiftName; } friend bool operator!=(const CommonEntityInfo &lhs, @@ -103,6 +107,10 @@ class CommonEntityInfo { } } + if (rhs.SwiftName.length() != 0 && + lhs.SwiftName.length() == 0) + lhs.SwiftName = rhs.SwiftName; + return lhs; } diff --git a/lib/APINotes/APINotesFormat.h b/lib/APINotes/APINotesFormat.h index 0801d2ceefd..79241042951 100644 --- a/lib/APINotes/APINotesFormat.h +++ b/lib/APINotes/APINotesFormat.h @@ -35,7 +35,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 7; +const uint16_t VERSION_MINOR = 8; using IdentifierID = Fixnum<31>; using IdentifierIDField = BCVBR<16>; diff --git a/lib/APINotes/APINotesReader.cpp b/lib/APINotes/APINotesReader.cpp index c768f3a3df7..25831287c02 100644 --- a/lib/APINotes/APINotesReader.cpp +++ b/lib/APINotes/APINotesReader.cpp @@ -39,6 +39,13 @@ namespace { = std::string(reinterpret_cast(data), reinterpret_cast(data) + msgLength); data += msgLength; + + unsigned swiftNameLength + = endian::readNext(data); + info.SwiftName + = std::string(reinterpret_cast(data), + reinterpret_cast(data) + swiftNameLength); + data += swiftNameLength; } /// Used to deserialize the on-disk identifier table. diff --git a/lib/APINotes/APINotesWriter.cpp b/lib/APINotes/APINotesWriter.cpp index 0e498a797a3..a9d83d95fd9 100644 --- a/lib/APINotes/APINotesWriter.cpp +++ b/lib/APINotes/APINotesWriter.cpp @@ -265,7 +265,7 @@ namespace { /// Retrieve the serialized size of the given CommonEntityInfo, for use in /// on-disk hash tables. static unsigned getCommonEntityInfoSize(const CommonEntityInfo &info) { - return 3 + info.UnavailableMsg.size(); + return 5 + info.UnavailableMsg.size() + info.SwiftName.size(); } /// Emit a serialized representation of the common entity information. @@ -275,6 +275,8 @@ namespace { writer.write(info.Unavailable << 1 | info.UnavailableInSwift); writer.write(info.UnavailableMsg.size()); out.write(info.UnavailableMsg.c_str(), info.UnavailableMsg.size()); + writer.write(info.SwiftName.size()); + out.write(info.SwiftName.c_str(), info.SwiftName.size()); } /// Used to serialize the on-disk Objective-C context table. diff --git a/lib/APINotes/APINotesYAMLCompiler.cpp b/lib/APINotes/APINotesYAMLCompiler.cpp index 8c27a01e5ed..8896c40cda0 100644 --- a/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/lib/APINotes/APINotesYAMLCompiler.cpp @@ -168,6 +168,7 @@ namespace { NullabilitySeq Nullability; llvm::Optional NullabilityOfRet; AvailabilityItem Availability; + StringRef SwiftName; api_notes::FactoryAsInitKind FactoryAsInit = api_notes::FactoryAsInitKind::Infer; bool DesignatedInit = false; @@ -179,6 +180,7 @@ namespace { StringRef Name; llvm::Optional Nullability; AvailabilityItem Availability; + StringRef SwiftName; }; typedef std::vector PropertiesSeq; @@ -186,6 +188,7 @@ namespace { StringRef Name; bool AuditedForNullability = false; AvailabilityItem Availability; + StringRef SwiftName; MethodsSeq Methods; PropertiesSeq Properties; }; @@ -196,6 +199,7 @@ namespace { NullabilitySeq Nullability; llvm::Optional NullabilityOfRet; AvailabilityItem Availability; + StringRef SwiftName; }; typedef std::vector FunctionsSeq; @@ -203,6 +207,7 @@ namespace { StringRef Name; llvm::Optional Nullability; AvailabilityItem Availability; + StringRef SwiftName; }; typedef std::vector GlobalVariablesSeq; @@ -278,6 +283,7 @@ namespace llvm { AbsentNullability); io.mapOptional("Availability", p.Availability.Mode); io.mapOptional("AvailabilityMsg", p.Availability.Msg); + io.mapOptional("SwiftName", p.SwiftName); } }; @@ -291,6 +297,7 @@ namespace llvm { AbsentNullability); io.mapOptional("Availability", m.Availability.Mode); io.mapOptional("AvailabilityMsg", m.Availability.Msg); + io.mapOptional("SwiftName", m.SwiftName); io.mapOptional("FactoryAsInit", m.FactoryAsInit, api_notes::FactoryAsInitKind::Infer); io.mapOptional("DesignatedInit", m.DesignatedInit, false); @@ -305,6 +312,7 @@ namespace llvm { io.mapOptional("AuditedForNullability", c.AuditedForNullability, false); io.mapOptional("Availability", c.Availability.Mode); io.mapOptional("AvailabilityMsg", c.Availability.Msg); + io.mapOptional("SwiftName", c.SwiftName); io.mapOptional("Methods", c.Methods); io.mapOptional("Properties", c.Properties); } @@ -319,6 +327,7 @@ namespace llvm { AbsentNullability); io.mapOptional("Availability", f.Availability.Mode); io.mapOptional("AvailabilityMsg", f.Availability.Msg); + io.mapOptional("SwiftName", f.SwiftName); } }; @@ -330,6 +339,7 @@ namespace llvm { AbsentNullability); io.mapOptional("Availability", v.Availability.Mode); io.mapOptional("AvailabilityMsg", v.Availability.Msg); + io.mapOptional("SwiftName", v.SwiftName); } }; @@ -463,6 +473,7 @@ static bool compile(const Module &module, return; convertAvailability(meth.Availability, mInfo, meth.Selector); + mInfo.SwiftName = meth.SwiftName; // Check if the selector ends with ':' to determine if it takes arguments. bool takesArguments = meth.Selector.endswith(":"); @@ -505,6 +516,7 @@ static bool compile(const Module &module, return; convertAvailability(cl.Availability, cInfo, cl.Name); + cInfo.SwiftName = cl.SwiftName; if (cl.AuditedForNullability) cInfo.setDefaultNullability(*DefaultNullability); @@ -545,6 +557,7 @@ static bool compile(const Module &module, if (!isAvailable(prop.Availability)) continue; convertAvailability(prop.Availability, pInfo, prop.Name); + pInfo.SwiftName = prop.SwiftName; if (prop.Nullability) pInfo.setNullabilityAudited(*prop.Nullability); Writer->addObjCProperty(clID, prop.Name, pInfo); @@ -598,6 +611,7 @@ static bool compile(const Module &module, if (!isAvailable(global.Availability)) continue; convertAvailability(global.Availability, info, global.Name); + info.SwiftName = global.SwiftName; if (global.Nullability) info.setNullabilityAudited(*global.Nullability); Writer->addGlobalVariable(global.Name, info); @@ -617,6 +631,7 @@ static bool compile(const Module &module, if (!isAvailable(function.Availability)) continue; convertAvailability(function.Availability, info, function.Name); + info.SwiftName = function.SwiftName; convertNullability(function.Nullability, function.NullabilityOfRet, info, function.Name); @@ -707,6 +722,8 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, // Handle class information. handleAvailability(record.Availability, info); + record.SwiftName = copyString(info.SwiftName); + if (info.getDefaultNullability()) { record.AuditedForNullability = true; } @@ -771,6 +788,7 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, handleNullability(method.Nullability, method.NullabilityOfRet, info, selector.count(':')); handleAvailability(method.Availability, info); + method.SwiftName = copyString(info.SwiftName); method.FactoryAsInit = info.getFactoryAsInitKind(); method.DesignatedInit = info.DesignatedInit; method.Required = info.Required; @@ -787,6 +805,7 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, Property property; property.Name = name; handleAvailability(property.Availability, info); + property.SwiftName = copyString(info.SwiftName); // FIXME: No way to represent "not audited for nullability". if (auto nullability = info.getNullability()) { @@ -805,6 +824,7 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, Function function; function.Name = name; handleAvailability(function.Availability, info); + function.SwiftName = copyString(info.SwiftName); if (info.NumAdjustedNullable > 0) handleNullability(function.Nullability, function.NullabilityOfRet, info, info.NumAdjustedNullable-1); @@ -817,6 +837,7 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, GlobalVariable global; global.Name = name; handleAvailability(global.Availability, info); + global.SwiftName = copyString(info.SwiftName); // FIXME: No way to represent "not audited for nullability". if (auto nullability = info.getNullability()) { diff --git a/lib/Sema/SemaAPINotes.cpp b/lib/Sema/SemaAPINotes.cpp index 6f08fd4c923..4faabd093f8 100644 --- a/lib/Sema/SemaAPINotes.cpp +++ b/lib/Sema/SemaAPINotes.cpp @@ -93,21 +93,38 @@ static void applyNullability(Sema &S, Decl *decl, NullabilityKind nullability) { } } +/// 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(mem), string.size()); +} + static void ProcessAPINotes(Sema &S, Decl *D, const api_notes::CommonEntityInfo &Info) { // Availability if (Info.Unavailable && !D->hasAttr()) { - D->addAttr(UnavailableAttr::CreateImplicit(S.Context, Info.UnavailableMsg)); + D->addAttr(UnavailableAttr::CreateImplicit(S.Context, + CopyString(S.Context, + Info.UnavailableMsg))); } if (Info.UnavailableInSwift) { - D->addAttr(AvailabilityAttr::CreateImplicit(S.Context, - &S.Context.Idents.get("swift"), - VersionTuple(), - VersionTuple(), - VersionTuple(), - /*Unavailable=*/true, - Info.UnavailableMsg)); + D->addAttr(AvailabilityAttr::CreateImplicit( + S.Context, + &S.Context.Idents.get("swift"), + VersionTuple(), + VersionTuple(), + VersionTuple(), + /*Unavailable=*/true, + CopyString(S.Context, Info.UnavailableMsg))); + } + + // swift_name + if (!Info.SwiftName.empty() && !D->hasAttr()) { + D->addAttr(SwiftNameAttr::CreateImplicit(S.Context, + CopyString(S.Context, + Info.SwiftName))); } } diff --git a/test/APINotes/Inputs/roundtrip.apinotes b/test/APINotes/Inputs/roundtrip.apinotes index b1722406663..a1b60e5b19f 100644 --- a/test/APINotes/Inputs/roundtrip.apinotes +++ b/test/APINotes/Inputs/roundtrip.apinotes @@ -6,12 +6,14 @@ Classes: - Name: NSCell Availability: available AvailabilityMsg: '' + SwiftName: '' Methods: - Selector: init MethodKind: Instance NullabilityOfRet: U Availability: available AvailabilityMsg: '' + SwiftName: '' DesignatedInit: true - Selector: 'initImageCell:' MethodKind: Instance @@ -19,6 +21,7 @@ Classes: NullabilityOfRet: U Availability: available AvailabilityMsg: '' + SwiftName: '' DesignatedInit: true - Selector: 'initTextCell:' MethodKind: Instance @@ -26,6 +29,7 @@ Classes: NullabilityOfRet: U Availability: available AvailabilityMsg: '' + SwiftName: '' DesignatedInit: true - Selector: 'initWithCoder:' MethodKind: Instance @@ -33,12 +37,14 @@ Classes: NullabilityOfRet: U Availability: available AvailabilityMsg: '' + SwiftName: '' DesignatedInit: true Required: true - Name: NSView AuditedForNullability: true Availability: available AvailabilityMsg: '' + SwiftName: '' Methods: - Selector: 'addSubview:' MethodKind: Instance @@ -46,34 +52,41 @@ Classes: NullabilityOfRet: N Availability: available AvailabilityMsg: '' + SwiftName: '' - Selector: 'addSubview:positioned:relativeTo:' MethodKind: Instance Nullability: [ N, N, O ] NullabilityOfRet: N Availability: available AvailabilityMsg: '' + SwiftName: '' - Selector: 'beginDraggingSessionWithItems:event:source:' MethodKind: Instance Nullability: [ U, U, N ] NullabilityOfRet: N Availability: available AvailabilityMsg: '' + SwiftName: 'beginDragginSession(_:event:source:)' Properties: - Name: enclosingScrollView Nullability: O Availability: available AvailabilityMsg: '' + SwiftName: enclosing - Name: makeBackingLayer Nullability: N Availability: available AvailabilityMsg: '' + SwiftName: '' Functions: - Name: NSAvailableWindowDepths NullabilityOfRet: N Availability: available AvailabilityMsg: '' + SwiftName: 'availableWindowDepths()' Globals: - Name: NSCalibratedWhiteColorSpace Nullability: N Availability: available AvailabilityMsg: '' + SwiftName: calibratedWhite From fc6a0facc32d3bd433a4d3b9b5536b6cab25afaa Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Sun, 21 Feb 2016 05:31:05 +0000 Subject: [PATCH 194/742] Class Property: Fix a crash with old ABI when generating metadata in classes. rdar://23891898 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261466 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CGObjCMac.cpp | 6 ++++-- test/CodeGenObjC/metadata-class-properties.m | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index bde9279a6d6..82c49c7dc8c 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/lib/CodeGen/CGObjCMac.cpp @@ -3448,8 +3448,10 @@ CGObjCMac::EmitClassExtension(const ObjCImplementationDecl *ID, llvm::Constant *Values[3]; Values[0] = llvm::ConstantInt::get(ObjCTypes.IntTy, Size); - Values[1] = nullptr; - if (!isClassProperty) + if (isClassProperty) { + llvm::Type *PtrTy = CGM.Int8PtrTy; + Values[1] = llvm::Constant::getNullValue(PtrTy); + } else Values[1] = BuildWeakIvarLayout(ID, CharUnits::Zero(), InstanceSize, hasMRCWeakIvars); if (isClassProperty) diff --git a/test/CodeGenObjC/metadata-class-properties.m b/test/CodeGenObjC/metadata-class-properties.m index 93557662355..aa5ed02ebe1 100644 --- a/test/CodeGenObjC/metadata-class-properties.m +++ b/test/CodeGenObjC/metadata-class-properties.m @@ -6,6 +6,9 @@ // CHECK: @"\01l_OBJC_$_CLASS_PROP_LIST_Foo_$_Category" = private global {{.*}} section "__DATA, __objc_const", align 8 // CHECK: @"\01l_OBJC_$_CATEGORY_Foo_$_Category" = private global %struct._category_t { {{.*}} @"\01l_OBJC_$_CLASS_PROP_LIST_Foo_$_Category" {{.*}} }, section "__DATA, __objc_const", align 8 +// CHECK: @"\01l_OBJC_$_CLASS_PROP_LIST_C" = private global {{.*}} section "__DATA, __objc_const", align 8 +// CHECK: @"\01l_OBJC_METACLASS_RO_$_C" = private global %struct._class_ro_t { {{.*}} @"\01l_OBJC_$_CLASS_PROP_LIST_C" {{.*}} }, section "__DATA, __objc_const", align 8 + // CHECK: !{i32 1, !"Objective-C Class Properties", i32 64} // CHECK-FRAGILE: @"OBJC_$_CLASS_PROP_PROTO_LIST_Proto" = private global {{.*}} section "__OBJC,__property,regular,no_dead_strip", align 8 @@ -13,6 +16,9 @@ // CHECK-FRAGILE: @"\01l_OBJC_$_CLASS_PROP_LIST_Foo_Category" = private global {{.*}} section "__OBJC,__property,regular,no_dead_strip", align 8 // CHECK-FRAGILE: @OBJC_CATEGORY_Foo_Category = private global %struct._objc_category { {{.*}}, i32 64, {{.*}} @"\01l_OBJC_$_CLASS_PROP_LIST_Foo_Category" {{.*}} }, section "__OBJC,__category,regular,no_dead_strip", align 8 +// CHECK-FRAGILE: @"\01l_OBJC_$_CLASS_PROP_LIST_C" = private global {{.*}} section "__OBJC,__property,regular,no_dead_strip", align 8 +// CHECK-FRAGILE: @OBJC_CLASSEXT_C = private global %struct._objc_class_extension { {{.*}} @"\01l_OBJC_$_CLASS_PROP_LIST_C" {{.*}} }, section "__OBJC,__class_ext,regular,no_dead_strip", align 8 + // CHECK-FRAGILE: !{i32 1, !"Objective-C Class Properties", i32 64} @interface Foo @end @@ -26,3 +32,11 @@ @interface Foo (Category) @end @implementation Foo (Category) +(int)proto_property { return 0; } @end + +__attribute__((objc_root_class)) +@interface C +@property(class, readonly) int p; +@end +@implementation C ++(int)p { return 1; } +@end From fe316e0d7aeb83d1cdd139355460363a4c4a15d4 Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Wed, 17 Feb 2016 22:05:48 +0000 Subject: [PATCH 195/742] Add 'nopartial' qualifier for availability attributes. An optional nopartial can be placed after the platform name. int bar() __attribute__((availability(macosx,nopartial,introduced=10.12)) When deploying back to a platform version prior to when the declaration was introduced, with 'nopartial', Clang emits an error specifying that the function is not introduced yet; without 'nopartial', the behavior stays the same: the declaration is `weakly linked`. A member is added to the end of AttributeList to save the location of the 'nopartial' keyword. A bool member is added to AvailabilityAttr. The diagnostics for 'nopartial' not-yet-introduced is handled in the same way as we handle unavailable cases. Reviewed by Doug Gregor and Jordan Rose. rdar://23791325 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261163 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/Attr.td | 3 +- include/clang/Basic/AttrDocs.td | 11 +++++-- include/clang/Basic/DiagnosticGroups.td | 1 + include/clang/Basic/DiagnosticSemaKinds.td | 9 ++++-- include/clang/Parse/Parser.h | 3 ++ include/clang/Sema/AttributeList.h | 32 ++++++++++++++++---- include/clang/Sema/DelayedDiagnostic.h | 9 ++++-- include/clang/Sema/Sema.h | 4 ++- lib/Parse/ParseDecl.cpp | 24 +++++++++++---- lib/Parse/Parser.cpp | 1 + lib/Sema/DelayedDiagnostic.cpp | 4 +++ lib/Sema/SemaDecl.cpp | 2 +- lib/Sema/SemaDeclAttr.cpp | 35 ++++++++++++++++++---- lib/Sema/SemaExpr.cpp | 13 ++++++-- test/Sema/attr-availability-macosx.c | 2 ++ 15 files changed, 124 insertions(+), 29 deletions(-) diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index c6126866fed..4d523effc26 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -453,7 +453,8 @@ def Availability : InheritableAttr { let Spellings = [GNU<"availability">]; let Args = [IdentifierArgument<"platform">, VersionArgument<"introduced">, VersionArgument<"deprecated">, VersionArgument<"obsoleted">, - BoolArgument<"unavailable">, StringArgument<"message">]; + BoolArgument<"unavailable">, StringArgument<"message">, + BoolArgument<"nopartial">]; let AdditionalMembers = [{static llvm::StringRef getPrettyPlatformName(llvm::StringRef Platform) { return llvm::StringSwitch(Platform) diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td index 08cd99b9867..07769eebff6 100644 --- a/include/clang/Basic/AttrDocs.td +++ b/include/clang/Basic/AttrDocs.td @@ -685,9 +685,14 @@ are: Apple's watchOS operating system. The minimum deployment target is specified by the ``-mwatchos-version-min=*version*`` command-line argument. -A declaration can be used even when deploying back to a platform version prior -to when the declaration was introduced. When this happens, the declaration is -`weakly linked +An optional nopartial can be placed after the platform name. +With the optional nopartial, when deploying back to a platform version prior to +when the declaration was introduced, Clang emits an error specifying that the +function is not introduced yet. + +Without the optional nopartial, a declaration can be used even when deploying back +to a platform version prior to when the declaration was introduced. When this +happens, the declaration is `weakly linked `_, as if the ``weak_import`` attribute were added to the declaration. A weakly-linked declaration may or may not be present a run-time, and a program diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td index 8e5f57d6d89..950e04b43f1 100644 --- a/include/clang/Basic/DiagnosticGroups.td +++ b/include/clang/Basic/DiagnosticGroups.td @@ -87,6 +87,7 @@ def DeprecatedAttributes : DiagGroup<"deprecated-attributes">; def DeprecatedDeclarations : DiagGroup<"deprecated-declarations">; def UnavailableDeclarations : DiagGroup<"unavailable-declarations">; def PartialAvailability : DiagGroup<"partial-availability">; +def NotYetIntroducedDeclarations : DiagGroup<"not-yet-introduced-declarations">; def DeprecatedImplementations :DiagGroup<"deprecated-implementations">; def DeprecatedIncrementBool : DiagGroup<"deprecated-increment-bool">; def DeprecatedRegister : DiagGroup<"deprecated-register">; diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index dd9fb576255..d70adafba70 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -837,7 +837,7 @@ def warn_accessor_property_type_mismatch : Warning< def not_conv_function_declared_at : Note<"type conversion function declared here">; def note_method_declared_at : Note<"method %0 declared here">; def note_property_attribute : Note<"property %0 is declared " - "%select{deprecated|unavailable|partial}1 here">; + "%select{deprecated|unavailable|partial|not-yet-introduced}1 here">; def err_setter_type_void : Error<"type of setter must be void">; def err_duplicate_method_decl : Error<"duplicate declaration of method %0">; def warn_duplicate_method_decl : @@ -4134,9 +4134,14 @@ def err_unavailable_message : Error<"%0 is unavailable: %1">; def warn_unavailable_fwdclass_message : Warning< "%0 may be unavailable because the receiver type is unknown">, InGroup; +def err_notyetintroduced : Error<"%0 is not introduced yet">; +def err_notyetintroduced_message : Error<"%0 is not introduced yet: %1">; +def warn_notyetintroduced_fwdclass_message : Warning< + "%0 may not be introduced because the receiver type is unknown">, + InGroup; def note_availability_specified_here : Note< "%0 has been explicitly marked " - "%select{unavailable|deleted|deprecated|partial}1 here">; + "%select{unavailable|deleted|deprecated|partial|not-yet-introduced}1 here">; def note_implicitly_deleted : Note< "explicitly defaulted function was implicitly deleted here">; def note_inherited_deleted_here : Note< diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 00885a5c710..10d6fa10680 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -134,6 +134,9 @@ class Parser : public CodeCompletionHandler { /// \brief Identifier for "message". IdentifierInfo *Ident_message; + /// \brief Identifier for "nopartial". + IdentifierInfo *Ident_nopartial; + /// C++0x contextual keywords. mutable IdentifierInfo *Ident_final; mutable IdentifierInfo *Ident_override; diff --git a/include/clang/Sema/AttributeList.h b/include/clang/Sema/AttributeList.h index 1a53a2c3d4a..7a160b819c2 100644 --- a/include/clang/Sema/AttributeList.h +++ b/include/clang/Sema/AttributeList.h @@ -157,6 +157,17 @@ class AttributeList { // TODO: This should really be called ParsedAttribute + NumArgs)[index]; } + /// The location of the 'nopartial' keyword in an availability attribute. + SourceLocation *getNopartialSlot() { + return reinterpret_cast( + &getAvailabilitySlot(ObsoletedSlot) + 1); + } + + SourceLocation const *getNopartialSlot() const { + return reinterpret_cast( + &getAvailabilitySlot(ObsoletedSlot) + 1); + } + public: struct TypeTagForDatatypeData { ParsedType *MatchingCType; @@ -233,7 +244,7 @@ class AttributeList { // TODO: This should really be called ParsedAttribute const AvailabilityChange &obsoleted, SourceLocation unavailable, const Expr *messageExpr, - Syntax syntaxUsed) + Syntax syntaxUsed, SourceLocation nopartial) : AttrName(attrName), ScopeName(scopeName), AttrRange(attrRange), ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(1), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(true), @@ -245,6 +256,7 @@ class AttributeList { // TODO: This should really be called ParsedAttribute new (&getAvailabilitySlot(IntroducedSlot)) AvailabilityChange(introduced); new (&getAvailabilitySlot(DeprecatedSlot)) AvailabilityChange(deprecated); new (&getAvailabilitySlot(ObsoletedSlot)) AvailabilityChange(obsoleted); + memcpy(getNopartialSlot(), &nopartial, sizeof(SourceLocation)); AttrKind = getKind(getName(), getScopeName(), syntaxUsed); } @@ -412,6 +424,11 @@ class AttributeList { // TODO: This should really be called ParsedAttribute return getAvailabilitySlot(ObsoletedSlot); } + SourceLocation getNopartialLoc() const { + assert(getKind() == AT_Availability && "Not an availability attribute"); + return *getNopartialSlot(); + } + SourceLocation getUnavailableLoc() const { assert(getKind() == AT_Availability && "Not an availability attribute"); return UnavailableLoc; @@ -488,7 +505,7 @@ class AttributeFactory { AvailabilityAllocSize = sizeof(AttributeList) + ((3 * sizeof(AvailabilityChange) + sizeof(void*) + - sizeof(ArgsUnion) - 1) + sizeof(ArgsUnion) + sizeof(SourceLocation) - 1) / sizeof(void*) * sizeof(void*)), TypeTagForDatatypeAllocSize = sizeof(AttributeList) @@ -606,13 +623,14 @@ class AttributePool { const AvailabilityChange &obsoleted, SourceLocation unavailable, const Expr *MessageExpr, - AttributeList::Syntax syntax) { + AttributeList::Syntax syntax, + SourceLocation nopartial) { void *memory = allocate(AttributeFactory::AvailabilityAllocSize); return add(new (memory) AttributeList(attrName, attrRange, scopeName, scopeLoc, Param, introduced, deprecated, obsoleted, unavailable, MessageExpr, - syntax)); + syntax, nopartial)); } AttributeList *create(IdentifierInfo *attrName, SourceRange attrRange, @@ -741,10 +759,12 @@ class ParsedAttributes { const AvailabilityChange &obsoleted, SourceLocation unavailable, const Expr *MessageExpr, - AttributeList::Syntax syntax) { + AttributeList::Syntax syntax, + SourceLocation nopartial) { AttributeList *attr = pool.create(attrName, attrRange, scopeName, scopeLoc, Param, introduced, - deprecated, obsoleted, unavailable, MessageExpr, syntax); + deprecated, obsoleted, unavailable, MessageExpr, syntax, + nopartial); add(attr); return attr; } diff --git a/include/clang/Sema/DelayedDiagnostic.h b/include/clang/Sema/DelayedDiagnostic.h index 155b3aa72d7..43d67640504 100644 --- a/include/clang/Sema/DelayedDiagnostic.h +++ b/include/clang/Sema/DelayedDiagnostic.h @@ -113,7 +113,8 @@ class AccessedEntity { /// the complete parsing of the current declaration. class DelayedDiagnostic { public: - enum DDKind { Deprecation, Unavailable, Access, ForbiddenType }; + enum DDKind { Deprecation, Unavailable, Access, ForbiddenType, + NotYetIntroduced }; unsigned char Kind; // actually a DDKind bool Triggered; @@ -165,13 +166,15 @@ class DelayedDiagnostic { } const NamedDecl *getDeprecationDecl() const { - assert((Kind == Deprecation || Kind == Unavailable) && + assert((Kind == Deprecation || Kind == Unavailable || + Kind == NotYetIntroduced) && "Not a deprecation diagnostic."); return DeprecationData.Decl; } StringRef getDeprecationMessage() const { - assert((Kind == Deprecation || Kind == Unavailable) && + assert((Kind == Deprecation || Kind == Unavailable || + Kind == NotYetIntroduced) && "Not a deprecation diagnostic."); return StringRef(DeprecationData.Message, DeprecationData.MessageLen); diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index ebdffc6f19f..e6a58b03344 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -2110,6 +2110,7 @@ class Sema { VersionTuple Obsoleted, bool IsUnavailable, StringRef Message, + bool IsNopartial, AvailabilityMergeKind AMK, unsigned AttrSpellingListIndex); TypeVisibilityAttr *mergeTypeVisibilityAttr(Decl *D, SourceRange Range, @@ -3547,7 +3548,8 @@ class Sema { void redelayDiagnostics(sema::DelayedDiagnosticPool &pool); - enum AvailabilityDiagnostic { AD_Deprecation, AD_Unavailable, AD_Partial }; + enum AvailabilityDiagnostic { AD_Deprecation, AD_Unavailable, AD_Partial, + AD_NotYetIntroduced }; void EmitAvailabilityWarning(AvailabilityDiagnostic AD, NamedDecl *D, StringRef Message, diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 7b7adac2b75..5adc9ac7a15 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -833,11 +833,14 @@ VersionTuple Parser::ParseVersionTuple(SourceRange &Range) { /// \brief Parse the contents of the "availability" attribute. /// /// availability-attribute: -/// 'availability' '(' platform ',' version-arg-list, opt-message')' +/// 'availability' '(' platform ',' opt-nopartial version-arg-list, opt-message')' /// /// platform: /// identifier /// +/// opt-nopartial: +/// 'nopartial' ',' +/// /// version-arg-list: /// version-arg /// version-arg ',' version-arg-list @@ -867,7 +870,7 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability, return; } - // Parse the platform name, + // Parse the platform name. if (Tok.isNot(tok::identifier)) { Diag(Tok, diag::err_availability_expected_platform); SkipUntil(tok::r_paren, StopAtSemi); @@ -889,10 +892,12 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability, Ident_obsoleted = PP.getIdentifierInfo("obsoleted"); Ident_unavailable = PP.getIdentifierInfo("unavailable"); Ident_message = PP.getIdentifierInfo("message"); + Ident_nopartial = PP.getIdentifierInfo("nopartial"); } - // Parse the set of introductions/deprecations/removals. - SourceLocation UnavailableLoc; + // Parse the optional "nopartial" and the set of + // introductions/deprecations/removals. + SourceLocation UnavailableLoc, NopartialLoc; do { if (Tok.isNot(tok::identifier)) { Diag(Tok, diag::err_availability_expected_change); @@ -902,6 +907,15 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability, IdentifierInfo *Keyword = Tok.getIdentifierInfo(); SourceLocation KeywordLoc = ConsumeToken(); + if (Keyword == Ident_nopartial) { + if (NopartialLoc.isValid()) { + Diag(KeywordLoc, diag::err_availability_redundant) + << Keyword << SourceRange(NopartialLoc); + } + NopartialLoc = KeywordLoc; + continue; + } + if (Keyword == Ident_unavailable) { if (UnavailableLoc.isValid()) { Diag(KeywordLoc, diag::err_availability_redundant) @@ -1023,7 +1037,7 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability, Changes[Deprecated], Changes[Obsoleted], UnavailableLoc, MessageExpr.get(), - Syntax); + Syntax, NopartialLoc); } /// \brief Parse the contents of the "objc_bridge_related" attribute. diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 5eb83837bc3..cc9d9a8a9d2 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -491,6 +491,7 @@ void Parser::Initialize() { Ident_deprecated = nullptr; Ident_obsoleted = nullptr; Ident_unavailable = nullptr; + Ident_nopartial = nullptr; Ident__except = nullptr; diff --git a/lib/Sema/DelayedDiagnostic.cpp b/lib/Sema/DelayedDiagnostic.cpp index ceea04f276c..853f5980545 100644 --- a/lib/Sema/DelayedDiagnostic.cpp +++ b/lib/Sema/DelayedDiagnostic.cpp @@ -35,6 +35,9 @@ DelayedDiagnostic::makeAvailability(Sema::AvailabilityDiagnostic AD, case Sema::AD_Unavailable: DD.Kind = Unavailable; break; + case Sema::AD_NotYetIntroduced: + DD.Kind = NotYetIntroduced; + break; case Sema::AD_Partial: llvm_unreachable("AD_Partial diags should not be delayed"); } @@ -63,6 +66,7 @@ void DelayedDiagnostic::Destroy() { case Deprecation: case Unavailable: + case NotYetIntroduced: delete [] DeprecationData.Message; break; diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index f8de19b9681..6e605419238 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -2196,7 +2196,7 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D, NewAttr = S.mergeAvailabilityAttr(D, AA->getRange(), AA->getPlatform(), AA->getIntroduced(), AA->getDeprecated(), AA->getObsoleted(), AA->getUnavailable(), - AA->getMessage(), AMK, + AA->getMessage(), AA->getNopartial(), AMK, AttrSpellingListIndex); else if (const auto *VA = dyn_cast(Attr)) NewAttr = S.mergeVisibilityAttr(D, VA->getRange(), VA->getVisibility(), diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index d58fdc7c13f..92a06390066 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -1935,6 +1935,7 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr(NamedDecl *D, SourceRange Range, VersionTuple Obsoleted, bool IsUnavailable, StringRef Message, + bool IsNopartial, AvailabilityMergeKind AMK, unsigned AttrSpellingListIndex) { VersionTuple MergedIntroduced = Introduced; @@ -2081,7 +2082,7 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr(NamedDecl *D, SourceRange Range, return ::new (Context) AvailabilityAttr(Range, Context, Platform, Introduced, Deprecated, Obsoleted, IsUnavailable, Message, - AttrSpellingListIndex); + IsNopartial, AttrSpellingListIndex); } return nullptr; } @@ -2108,6 +2109,7 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, AvailabilityChange Deprecated = Attr.getAvailabilityDeprecated(); AvailabilityChange Obsoleted = Attr.getAvailabilityObsoleted(); bool IsUnavailable = Attr.getUnavailableLoc().isValid(); + bool IsNopartial = Attr.getNopartialLoc().isValid(); StringRef Str; if (const StringLiteral *SE = dyn_cast_or_null(Attr.getMessageExpr())) @@ -2126,6 +2128,7 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, Deprecated.Version, Obsoleted.Version, IsUnavailable, Str, + IsNopartial, Sema::AMK_None, Index); if (NewAttr) @@ -2170,6 +2173,7 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, NewDeprecated, NewObsoleted, IsUnavailable, Str, + IsNopartial, Sema::AMK_None, Index); if (NewAttr) @@ -2192,6 +2196,7 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, Deprecated.Version, Obsoleted.Version, IsUnavailable, Str, + IsNopartial, Sema::AMK_None, Index); if (NewAttr) @@ -6246,6 +6251,14 @@ static void DoEmitAvailabilityWarning(Sema &S, Sema::AvailabilityDiagnostic K, property_note_select = /* partial */ 2; available_here_select_kind = /* partial */ 3; break; + + case Sema::AD_NotYetIntroduced: + diag = diag::err_notyetintroduced; + diag_message = diag::err_notyetintroduced_message; + diag_fwdclass_message = diag::warn_notyetintroduced_fwdclass_message; + property_note_select = /* deprecated */ 3; + available_here_select_kind = /* notyetintroduced */ 4; + break; } if (!Message.empty()) { @@ -6272,10 +6285,22 @@ static void DoEmitAvailabilityWarning(Sema &S, Sema::AvailabilityDiagnostic K, static void handleDelayedAvailabilityCheck(Sema &S, DelayedDiagnostic &DD, Decl *Ctx) { assert(DD.Kind == DelayedDiagnostic::Deprecation || - DD.Kind == DelayedDiagnostic::Unavailable); - Sema::AvailabilityDiagnostic AD = DD.Kind == DelayedDiagnostic::Deprecation - ? Sema::AD_Deprecation - : Sema::AD_Unavailable; + DD.Kind == DelayedDiagnostic::Unavailable || + DD.Kind == DelayedDiagnostic::NotYetIntroduced); + Sema::AvailabilityDiagnostic AD; + switch (DD.Kind) { + case DelayedDiagnostic::Deprecation: + AD = Sema::AD_Deprecation; + break; + case DelayedDiagnostic::Unavailable: + AD = Sema::AD_Unavailable; + break; + case DelayedDiagnostic::NotYetIntroduced: + AD = Sema::AD_NotYetIntroduced; + break; + default: + llvm_unreachable("Expecting: deprecated, unavailable, not-yet-introduced"); + } DD.Triggered = true; DoEmitAvailabilityWarning( S, AD, Ctx, DD.getDeprecationDecl(), DD.getDeprecationMessage(), DD.Loc, diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index d8dd0ba119b..744745c9fc7 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -137,7 +137,7 @@ DiagnoseAvailabilityOfDecl(Sema &S, NamedDecl *D, SourceLocation Loc, const ObjCPropertyDecl *ObjCPDecl = nullptr; if (Result == AR_Deprecated || Result == AR_Unavailable || - AR_NotYetIntroduced) { + Result == AR_NotYetIntroduced) { if (const ObjCMethodDecl *MD = dyn_cast(D)) { if (const ObjCPropertyDecl *PD = MD->findPropertyDecl()) { AvailabilityResult PDeclResult = PD->getAvailability(nullptr); @@ -159,11 +159,20 @@ DiagnoseAvailabilityOfDecl(Sema &S, NamedDecl *D, SourceLocation Loc, break; case AR_NotYetIntroduced: { + // With nopartial, the compiler will emit delayed error just like how + // "deprecated, unavailable" are handled. + AvailabilityAttr *AA = D->getAttr(); + if (AA && AA->getNopartial() && + S.getCurContextAvailability() != AR_NotYetIntroduced) + S.EmitAvailabilityWarning(Sema::AD_NotYetIntroduced, + D, Message, Loc, UnknownObjCClass, ObjCPDecl, + ObjCPropertyAccess); + // Don't do this for enums, they can't be redeclared. if (isa(D) || isa(D)) break; - bool Warn = !D->getAttr()->isInherited(); + bool Warn = !AA->isInherited(); // Objective-C method declarations in categories are not modelled as // redeclarations, so manually look for a redeclaration in a category // if necessary. diff --git a/test/Sema/attr-availability-macosx.c b/test/Sema/attr-availability-macosx.c index 38f149ba62a..8f5550ede2a 100644 --- a/test/Sema/attr-availability-macosx.c +++ b/test/Sema/attr-availability-macosx.c @@ -6,6 +6,7 @@ void f2(int) __attribute__((availability(macosx,introduced=10.4,deprecated=10.5) void f3(int) __attribute__((availability(macosx,introduced=10.6))); void f4(int) __attribute__((availability(macosx,introduced=10.1,deprecated=10.3,obsoleted=10.5), availability(ios,introduced=2.0,deprecated=3.0))); // expected-note{{explicitly marked unavailable}} void f5(int) __attribute__((availability(ios,introduced=3.2), availability(macosx,unavailable))); // expected-note{{'f5' has been explicitly marked unavailable here}} +void f6(int) __attribute__((availability(macosx,nopartial,introduced=10.6))); //expected-note{{'f6' has been explicitly marked not-yet-introduced here}} void test() { f0(0); @@ -14,6 +15,7 @@ void test() { f3(0); f4(0); // expected-error{{f4' is unavailable: obsoleted in OS X 10.5}} f5(0); // expected-error{{'f5' is unavailable: not available on OS X}} + f6(0); // expected-error{{'f6' is not introduced yet: introduced in OS X 10.6}} } // rdar://10535640 From d56be6ae3b107253332ce0a17be586412be42726 Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Mon, 22 Feb 2016 04:47:24 +0000 Subject: [PATCH 196/742] Addressing review comments for r261163. Use "strict" instead of "nopartial". Also make strictly not-introduced share the same diagnostics as Obsolete and Unavailable. rdar://23791325 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261512 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/Attr.td | 2 +- include/clang/Basic/AttrDocs.td | 17 ++++----- include/clang/Basic/DiagnosticGroups.td | 1 - include/clang/Basic/DiagnosticSemaKinds.td | 9 ++--- include/clang/Parse/Parser.h | 4 +-- include/clang/Sema/AttributeList.h | 22 ++++++------ include/clang/Sema/DelayedDiagnostic.h | 9 ++--- include/clang/Sema/Sema.h | 5 ++- lib/Parse/ParseDecl.cpp | 22 ++++++------ lib/Parse/Parser.cpp | 2 +- lib/Sema/DelayedDiagnostic.cpp | 4 --- lib/Sema/SemaDecl.cpp | 2 +- lib/Sema/SemaDeclAttr.cpp | 40 ++++++---------------- lib/Sema/SemaExpr.cpp | 7 ++-- test/Sema/attr-availability-macosx.c | 4 +-- 15 files changed, 58 insertions(+), 92 deletions(-) diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 4d523effc26..1b6641c2317 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -454,7 +454,7 @@ def Availability : InheritableAttr { let Args = [IdentifierArgument<"platform">, VersionArgument<"introduced">, VersionArgument<"deprecated">, VersionArgument<"obsoleted">, BoolArgument<"unavailable">, StringArgument<"message">, - BoolArgument<"nopartial">]; + BoolArgument<"strict">]; let AdditionalMembers = [{static llvm::StringRef getPrettyPlatformName(llvm::StringRef Platform) { return llvm::StringSwitch(Platform) diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td index 07769eebff6..dff98411962 100644 --- a/include/clang/Basic/AttrDocs.td +++ b/include/clang/Basic/AttrDocs.td @@ -685,20 +685,21 @@ are: Apple's watchOS operating system. The minimum deployment target is specified by the ``-mwatchos-version-min=*version*`` command-line argument. -An optional nopartial can be placed after the platform name. -With the optional nopartial, when deploying back to a platform version prior to -when the declaration was introduced, Clang emits an error specifying that the -function is not introduced yet. - -Without the optional nopartial, a declaration can be used even when deploying back -to a platform version prior to when the declaration was introduced. When this -happens, the declaration is `weakly linked +A declaration can typically be used even when deploying back to a platform +version prior to when the declaration was introduced. When this happens, the +declaration is `weakly linked `_, as if the ``weak_import`` attribute were added to the declaration. A weakly-linked declaration may or may not be present a run-time, and a program can determine whether the declaration is present by checking whether the address of that declaration is non-NULL. +The flag ``strict`` disallows using API when deploying back to a +platform version prior to when the declaration was introduced. An +attempt to use such API before its introduction causes a hard error. +Weakly-linking is almost always a better API choice, since it allows +users to query availability at runtime. + If there are multiple declarations of the same entity, the availability attributes must either match on a per-platform basis or later declarations must not have availability attributes for that diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td index 950e04b43f1..8e5f57d6d89 100644 --- a/include/clang/Basic/DiagnosticGroups.td +++ b/include/clang/Basic/DiagnosticGroups.td @@ -87,7 +87,6 @@ def DeprecatedAttributes : DiagGroup<"deprecated-attributes">; def DeprecatedDeclarations : DiagGroup<"deprecated-declarations">; def UnavailableDeclarations : DiagGroup<"unavailable-declarations">; def PartialAvailability : DiagGroup<"partial-availability">; -def NotYetIntroducedDeclarations : DiagGroup<"not-yet-introduced-declarations">; def DeprecatedImplementations :DiagGroup<"deprecated-implementations">; def DeprecatedIncrementBool : DiagGroup<"deprecated-increment-bool">; def DeprecatedRegister : DiagGroup<"deprecated-register">; diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index d70adafba70..dd9fb576255 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -837,7 +837,7 @@ def warn_accessor_property_type_mismatch : Warning< def not_conv_function_declared_at : Note<"type conversion function declared here">; def note_method_declared_at : Note<"method %0 declared here">; def note_property_attribute : Note<"property %0 is declared " - "%select{deprecated|unavailable|partial|not-yet-introduced}1 here">; + "%select{deprecated|unavailable|partial}1 here">; def err_setter_type_void : Error<"type of setter must be void">; def err_duplicate_method_decl : Error<"duplicate declaration of method %0">; def warn_duplicate_method_decl : @@ -4134,14 +4134,9 @@ def err_unavailable_message : Error<"%0 is unavailable: %1">; def warn_unavailable_fwdclass_message : Warning< "%0 may be unavailable because the receiver type is unknown">, InGroup; -def err_notyetintroduced : Error<"%0 is not introduced yet">; -def err_notyetintroduced_message : Error<"%0 is not introduced yet: %1">; -def warn_notyetintroduced_fwdclass_message : Warning< - "%0 may not be introduced because the receiver type is unknown">, - InGroup; def note_availability_specified_here : Note< "%0 has been explicitly marked " - "%select{unavailable|deleted|deprecated|partial|not-yet-introduced}1 here">; + "%select{unavailable|deleted|deprecated|partial}1 here">; def note_implicitly_deleted : Note< "explicitly defaulted function was implicitly deleted here">; def note_inherited_deleted_here : Note< diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 10d6fa10680..6ca6af91dbf 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -134,8 +134,8 @@ class Parser : public CodeCompletionHandler { /// \brief Identifier for "message". IdentifierInfo *Ident_message; - /// \brief Identifier for "nopartial". - IdentifierInfo *Ident_nopartial; + /// \brief Identifier for "strict". + IdentifierInfo *Ident_strict; /// C++0x contextual keywords. mutable IdentifierInfo *Ident_final; diff --git a/include/clang/Sema/AttributeList.h b/include/clang/Sema/AttributeList.h index 7a160b819c2..26527036404 100644 --- a/include/clang/Sema/AttributeList.h +++ b/include/clang/Sema/AttributeList.h @@ -157,13 +157,13 @@ class AttributeList { // TODO: This should really be called ParsedAttribute + NumArgs)[index]; } - /// The location of the 'nopartial' keyword in an availability attribute. - SourceLocation *getNopartialSlot() { + /// The location of the 'strict' keyword in an availability attribute. + SourceLocation *getStrictSlot() { return reinterpret_cast( &getAvailabilitySlot(ObsoletedSlot) + 1); } - SourceLocation const *getNopartialSlot() const { + SourceLocation const *getStrictSlot() const { return reinterpret_cast( &getAvailabilitySlot(ObsoletedSlot) + 1); } @@ -244,7 +244,7 @@ class AttributeList { // TODO: This should really be called ParsedAttribute const AvailabilityChange &obsoleted, SourceLocation unavailable, const Expr *messageExpr, - Syntax syntaxUsed, SourceLocation nopartial) + Syntax syntaxUsed, SourceLocation strict) : AttrName(attrName), ScopeName(scopeName), AttrRange(attrRange), ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(1), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(true), @@ -256,7 +256,7 @@ class AttributeList { // TODO: This should really be called ParsedAttribute new (&getAvailabilitySlot(IntroducedSlot)) AvailabilityChange(introduced); new (&getAvailabilitySlot(DeprecatedSlot)) AvailabilityChange(deprecated); new (&getAvailabilitySlot(ObsoletedSlot)) AvailabilityChange(obsoleted); - memcpy(getNopartialSlot(), &nopartial, sizeof(SourceLocation)); + memcpy(getStrictSlot(), &strict, sizeof(SourceLocation)); AttrKind = getKind(getName(), getScopeName(), syntaxUsed); } @@ -424,9 +424,9 @@ class AttributeList { // TODO: This should really be called ParsedAttribute return getAvailabilitySlot(ObsoletedSlot); } - SourceLocation getNopartialLoc() const { + SourceLocation getStrictLoc() const { assert(getKind() == AT_Availability && "Not an availability attribute"); - return *getNopartialSlot(); + return *getStrictSlot(); } SourceLocation getUnavailableLoc() const { @@ -624,13 +624,13 @@ class AttributePool { SourceLocation unavailable, const Expr *MessageExpr, AttributeList::Syntax syntax, - SourceLocation nopartial) { + SourceLocation strict) { void *memory = allocate(AttributeFactory::AvailabilityAllocSize); return add(new (memory) AttributeList(attrName, attrRange, scopeName, scopeLoc, Param, introduced, deprecated, obsoleted, unavailable, MessageExpr, - syntax, nopartial)); + syntax, strict)); } AttributeList *create(IdentifierInfo *attrName, SourceRange attrRange, @@ -760,11 +760,11 @@ class ParsedAttributes { SourceLocation unavailable, const Expr *MessageExpr, AttributeList::Syntax syntax, - SourceLocation nopartial) { + SourceLocation strict) { AttributeList *attr = pool.create(attrName, attrRange, scopeName, scopeLoc, Param, introduced, deprecated, obsoleted, unavailable, MessageExpr, syntax, - nopartial); + strict); add(attr); return attr; } diff --git a/include/clang/Sema/DelayedDiagnostic.h b/include/clang/Sema/DelayedDiagnostic.h index 43d67640504..155b3aa72d7 100644 --- a/include/clang/Sema/DelayedDiagnostic.h +++ b/include/clang/Sema/DelayedDiagnostic.h @@ -113,8 +113,7 @@ class AccessedEntity { /// the complete parsing of the current declaration. class DelayedDiagnostic { public: - enum DDKind { Deprecation, Unavailable, Access, ForbiddenType, - NotYetIntroduced }; + enum DDKind { Deprecation, Unavailable, Access, ForbiddenType }; unsigned char Kind; // actually a DDKind bool Triggered; @@ -166,15 +165,13 @@ class DelayedDiagnostic { } const NamedDecl *getDeprecationDecl() const { - assert((Kind == Deprecation || Kind == Unavailable || - Kind == NotYetIntroduced) && + assert((Kind == Deprecation || Kind == Unavailable) && "Not a deprecation diagnostic."); return DeprecationData.Decl; } StringRef getDeprecationMessage() const { - assert((Kind == Deprecation || Kind == Unavailable || - Kind == NotYetIntroduced) && + assert((Kind == Deprecation || Kind == Unavailable) && "Not a deprecation diagnostic."); return StringRef(DeprecationData.Message, DeprecationData.MessageLen); diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index e6a58b03344..54328efddd8 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -2110,7 +2110,7 @@ class Sema { VersionTuple Obsoleted, bool IsUnavailable, StringRef Message, - bool IsNopartial, + bool IsStrict, AvailabilityMergeKind AMK, unsigned AttrSpellingListIndex); TypeVisibilityAttr *mergeTypeVisibilityAttr(Decl *D, SourceRange Range, @@ -3548,8 +3548,7 @@ class Sema { void redelayDiagnostics(sema::DelayedDiagnosticPool &pool); - enum AvailabilityDiagnostic { AD_Deprecation, AD_Unavailable, AD_Partial, - AD_NotYetIntroduced }; + enum AvailabilityDiagnostic { AD_Deprecation, AD_Unavailable, AD_Partial }; void EmitAvailabilityWarning(AvailabilityDiagnostic AD, NamedDecl *D, StringRef Message, diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 5adc9ac7a15..f306294434e 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -833,13 +833,13 @@ VersionTuple Parser::ParseVersionTuple(SourceRange &Range) { /// \brief Parse the contents of the "availability" attribute. /// /// availability-attribute: -/// 'availability' '(' platform ',' opt-nopartial version-arg-list, opt-message')' +/// 'availability' '(' platform ',' opt-strict version-arg-list, opt-message')' /// /// platform: /// identifier /// -/// opt-nopartial: -/// 'nopartial' ',' +/// opt-strict: +/// 'strict' ',' /// /// version-arg-list: /// version-arg @@ -892,12 +892,12 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability, Ident_obsoleted = PP.getIdentifierInfo("obsoleted"); Ident_unavailable = PP.getIdentifierInfo("unavailable"); Ident_message = PP.getIdentifierInfo("message"); - Ident_nopartial = PP.getIdentifierInfo("nopartial"); + Ident_strict = PP.getIdentifierInfo("strict"); } - // Parse the optional "nopartial" and the set of + // Parse the optional "strict" and the set of // introductions/deprecations/removals. - SourceLocation UnavailableLoc, NopartialLoc; + SourceLocation UnavailableLoc, StrictLoc; do { if (Tok.isNot(tok::identifier)) { Diag(Tok, diag::err_availability_expected_change); @@ -907,12 +907,12 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability, IdentifierInfo *Keyword = Tok.getIdentifierInfo(); SourceLocation KeywordLoc = ConsumeToken(); - if (Keyword == Ident_nopartial) { - if (NopartialLoc.isValid()) { + if (Keyword == Ident_strict) { + if (StrictLoc.isValid()) { Diag(KeywordLoc, diag::err_availability_redundant) - << Keyword << SourceRange(NopartialLoc); + << Keyword << SourceRange(StrictLoc); } - NopartialLoc = KeywordLoc; + StrictLoc = KeywordLoc; continue; } @@ -1037,7 +1037,7 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability, Changes[Deprecated], Changes[Obsoleted], UnavailableLoc, MessageExpr.get(), - Syntax, NopartialLoc); + Syntax, StrictLoc); } /// \brief Parse the contents of the "objc_bridge_related" attribute. diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index cc9d9a8a9d2..ac4e52b3acb 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -491,7 +491,7 @@ void Parser::Initialize() { Ident_deprecated = nullptr; Ident_obsoleted = nullptr; Ident_unavailable = nullptr; - Ident_nopartial = nullptr; + Ident_strict = nullptr; Ident__except = nullptr; diff --git a/lib/Sema/DelayedDiagnostic.cpp b/lib/Sema/DelayedDiagnostic.cpp index 853f5980545..ceea04f276c 100644 --- a/lib/Sema/DelayedDiagnostic.cpp +++ b/lib/Sema/DelayedDiagnostic.cpp @@ -35,9 +35,6 @@ DelayedDiagnostic::makeAvailability(Sema::AvailabilityDiagnostic AD, case Sema::AD_Unavailable: DD.Kind = Unavailable; break; - case Sema::AD_NotYetIntroduced: - DD.Kind = NotYetIntroduced; - break; case Sema::AD_Partial: llvm_unreachable("AD_Partial diags should not be delayed"); } @@ -66,7 +63,6 @@ void DelayedDiagnostic::Destroy() { case Deprecation: case Unavailable: - case NotYetIntroduced: delete [] DeprecationData.Message; break; diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 6e605419238..62ed26006ec 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -2196,7 +2196,7 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D, NewAttr = S.mergeAvailabilityAttr(D, AA->getRange(), AA->getPlatform(), AA->getIntroduced(), AA->getDeprecated(), AA->getObsoleted(), AA->getUnavailable(), - AA->getMessage(), AA->getNopartial(), AMK, + AA->getMessage(), AA->getStrict(), AMK, AttrSpellingListIndex); else if (const auto *VA = dyn_cast(Attr)) NewAttr = S.mergeVisibilityAttr(D, VA->getRange(), VA->getVisibility(), diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 92a06390066..c45becf0ab4 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -1935,7 +1935,7 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr(NamedDecl *D, SourceRange Range, VersionTuple Obsoleted, bool IsUnavailable, StringRef Message, - bool IsNopartial, + bool IsStrict, AvailabilityMergeKind AMK, unsigned AttrSpellingListIndex) { VersionTuple MergedIntroduced = Introduced; @@ -2082,7 +2082,7 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr(NamedDecl *D, SourceRange Range, return ::new (Context) AvailabilityAttr(Range, Context, Platform, Introduced, Deprecated, Obsoleted, IsUnavailable, Message, - IsNopartial, AttrSpellingListIndex); + IsStrict, AttrSpellingListIndex); } return nullptr; } @@ -2109,7 +2109,7 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, AvailabilityChange Deprecated = Attr.getAvailabilityDeprecated(); AvailabilityChange Obsoleted = Attr.getAvailabilityObsoleted(); bool IsUnavailable = Attr.getUnavailableLoc().isValid(); - bool IsNopartial = Attr.getNopartialLoc().isValid(); + bool IsStrict = Attr.getStrictLoc().isValid(); StringRef Str; if (const StringLiteral *SE = dyn_cast_or_null(Attr.getMessageExpr())) @@ -2128,7 +2128,7 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, Deprecated.Version, Obsoleted.Version, IsUnavailable, Str, - IsNopartial, + IsStrict, Sema::AMK_None, Index); if (NewAttr) @@ -2173,7 +2173,7 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, NewDeprecated, NewObsoleted, IsUnavailable, Str, - IsNopartial, + IsStrict, Sema::AMK_None, Index); if (NewAttr) @@ -2196,7 +2196,7 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, Deprecated.Version, Obsoleted.Version, IsUnavailable, Str, - IsNopartial, + IsStrict, Sema::AMK_None, Index); if (NewAttr) @@ -6251,14 +6251,6 @@ static void DoEmitAvailabilityWarning(Sema &S, Sema::AvailabilityDiagnostic K, property_note_select = /* partial */ 2; available_here_select_kind = /* partial */ 3; break; - - case Sema::AD_NotYetIntroduced: - diag = diag::err_notyetintroduced; - diag_message = diag::err_notyetintroduced_message; - diag_fwdclass_message = diag::warn_notyetintroduced_fwdclass_message; - property_note_select = /* deprecated */ 3; - available_here_select_kind = /* notyetintroduced */ 4; - break; } if (!Message.empty()) { @@ -6285,22 +6277,10 @@ static void DoEmitAvailabilityWarning(Sema &S, Sema::AvailabilityDiagnostic K, static void handleDelayedAvailabilityCheck(Sema &S, DelayedDiagnostic &DD, Decl *Ctx) { assert(DD.Kind == DelayedDiagnostic::Deprecation || - DD.Kind == DelayedDiagnostic::Unavailable || - DD.Kind == DelayedDiagnostic::NotYetIntroduced); - Sema::AvailabilityDiagnostic AD; - switch (DD.Kind) { - case DelayedDiagnostic::Deprecation: - AD = Sema::AD_Deprecation; - break; - case DelayedDiagnostic::Unavailable: - AD = Sema::AD_Unavailable; - break; - case DelayedDiagnostic::NotYetIntroduced: - AD = Sema::AD_NotYetIntroduced; - break; - default: - llvm_unreachable("Expecting: deprecated, unavailable, not-yet-introduced"); - } + DD.Kind == DelayedDiagnostic::Unavailable); + Sema::AvailabilityDiagnostic AD = DD.Kind == DelayedDiagnostic::Deprecation + ? Sema::AD_Deprecation + : Sema::AD_Unavailable; DD.Triggered = true; DoEmitAvailabilityWarning( S, AD, Ctx, DD.getDeprecationDecl(), DD.getDeprecationMessage(), DD.Loc, diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 744745c9fc7..68ceac2ae62 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -159,12 +159,11 @@ DiagnoseAvailabilityOfDecl(Sema &S, NamedDecl *D, SourceLocation Loc, break; case AR_NotYetIntroduced: { - // With nopartial, the compiler will emit delayed error just like how - // "deprecated, unavailable" are handled. + // With strict, the compiler will emit unavailable error. AvailabilityAttr *AA = D->getAttr(); - if (AA && AA->getNopartial() && + if (AA && AA->getStrict() && S.getCurContextAvailability() != AR_NotYetIntroduced) - S.EmitAvailabilityWarning(Sema::AD_NotYetIntroduced, + S.EmitAvailabilityWarning(Sema::AD_Unavailable, D, Message, Loc, UnknownObjCClass, ObjCPDecl, ObjCPropertyAccess); diff --git a/test/Sema/attr-availability-macosx.c b/test/Sema/attr-availability-macosx.c index 8f5550ede2a..a177ef2972f 100644 --- a/test/Sema/attr-availability-macosx.c +++ b/test/Sema/attr-availability-macosx.c @@ -6,7 +6,7 @@ void f2(int) __attribute__((availability(macosx,introduced=10.4,deprecated=10.5) void f3(int) __attribute__((availability(macosx,introduced=10.6))); void f4(int) __attribute__((availability(macosx,introduced=10.1,deprecated=10.3,obsoleted=10.5), availability(ios,introduced=2.0,deprecated=3.0))); // expected-note{{explicitly marked unavailable}} void f5(int) __attribute__((availability(ios,introduced=3.2), availability(macosx,unavailable))); // expected-note{{'f5' has been explicitly marked unavailable here}} -void f6(int) __attribute__((availability(macosx,nopartial,introduced=10.6))); //expected-note{{'f6' has been explicitly marked not-yet-introduced here}} +void f6(int) __attribute__((availability(macosx,strict,introduced=10.6))); //expected-note{{'f6' has been explicitly marked unavailable here}} void test() { f0(0); @@ -15,7 +15,7 @@ void test() { f3(0); f4(0); // expected-error{{f4' is unavailable: obsoleted in OS X 10.5}} f5(0); // expected-error{{'f5' is unavailable: not available on OS X}} - f6(0); // expected-error{{'f6' is not introduced yet: introduced in OS X 10.6}} + f6(0); // expected-error{{'f6' is unavailable: introduced in OS X 10.6}} } // rdar://10535640 From 5d61660417343c64ca7523332b933a35b6ed5993 Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Mon, 22 Feb 2016 18:24:30 +0000 Subject: [PATCH 197/742] Add has_feature attribute_availability_with_strict. rdar://23791325 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261548 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Lex/PPMacroExpansion.cpp | 1 + test/Sema/attr-availability-macosx.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp index c11e37f5a9d..8ea4ed6c8f2 100644 --- a/lib/Lex/PPMacroExpansion.cpp +++ b/lib/Lex/PPMacroExpansion.cpp @@ -1063,6 +1063,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .Case("attribute_availability_tvos", true) .Case("attribute_availability_watchos", true) .Case("attribute_availability_swift", true) + .Case("attribute_availability_with_strict", true) .Case("attribute_cf_returns_not_retained", true) .Case("attribute_cf_returns_retained", true) .Case("attribute_cf_returns_on_parameters", true) diff --git a/test/Sema/attr-availability-macosx.c b/test/Sema/attr-availability-macosx.c index a177ef2972f..7efe7506926 100644 --- a/test/Sema/attr-availability-macosx.c +++ b/test/Sema/attr-availability-macosx.c @@ -1,5 +1,9 @@ // RUN: %clang_cc1 "-triple" "x86_64-apple-darwin9.0.0" -fsyntax-only -verify %s +#if !__has_feature(attribute_availability_with_strict) +#error "Missing __has_feature" +#endif + void f0(int) __attribute__((availability(macosx,introduced=10.4,deprecated=10.6))); void f1(int) __attribute__((availability(macosx,introduced=10.5))); void f2(int) __attribute__((availability(macosx,introduced=10.4,deprecated=10.5))); // expected-note {{'f2' has been explicitly marked deprecated here}} From fba3fa7296642b8aee11bb8ff170d59bfbf2781a Mon Sep 17 00:00:00 2001 From: "Duncan P. N. Exon Smith" Date: Sat, 20 Feb 2016 18:53:45 +0000 Subject: [PATCH 198/742] Lex: Remove explicitly deleted copy constructor, NFC `std::unique_ptr` already deletes these, so there's no reason for the boiler-plate in HeaderMap. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261442 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit a98ffff45be3a13a09c198de93b740aadb9bd40b) --- include/clang/Lex/HeaderMap.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/clang/Lex/HeaderMap.h b/include/clang/Lex/HeaderMap.h index 183361e4f8c..f212d82cc10 100644 --- a/include/clang/Lex/HeaderMap.h +++ b/include/clang/Lex/HeaderMap.h @@ -32,9 +32,6 @@ namespace clang { /// symlinks to files. Its advantages are that it is dense and more efficient /// to create and process than a directory of symlinks. class HeaderMap { - HeaderMap(const HeaderMap &) = delete; - void operator=(const HeaderMap &) = delete; - std::unique_ptr FileBuffer; bool NeedsBSwap; From 929b8ea97dbb9f15264f180a113142192a2eee0b Mon Sep 17 00:00:00 2001 From: "Duncan P. N. Exon Smith" Date: Sat, 20 Feb 2016 18:55:08 +0000 Subject: [PATCH 199/742] Lex: clang-format HeaderMap.h, NFC git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261443 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit d9d154cc11d04d1b6060592093208df8455e6562) --- include/clang/Lex/HeaderMap.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/include/clang/Lex/HeaderMap.h b/include/clang/Lex/HeaderMap.h index f212d82cc10..922a272bb87 100644 --- a/include/clang/Lex/HeaderMap.h +++ b/include/clang/Lex/HeaderMap.h @@ -22,10 +22,11 @@ namespace llvm { class MemoryBuffer; } namespace clang { - class FileEntry; - class FileManager; - struct HMapBucket; - struct HMapHeader; + +class FileEntry; +class FileManager; +struct HMapBucket; +struct HMapHeader; /// This class represents an Apple concept known as a 'header map'. To the /// \#include file resolution process, it basically acts like a directory of @@ -37,6 +38,7 @@ class HeaderMap { HeaderMap(std::unique_ptr File, bool BSwap) : FileBuffer(std::move(File)), NeedsBSwap(BSwap) {} + public: /// HeaderMap::Create - This attempts to load the specified file as a header /// map. If it doesn't look like a HeaderMap, it gives up and returns null. From 07845edd8ab4d6e470707fe04a29d281b6518c24 Mon Sep 17 00:00:00 2001 From: "Duncan P. N. Exon Smith" Date: Sat, 20 Feb 2016 20:39:51 +0000 Subject: [PATCH 200/742] Lex: Add some unit tests for corrupt header maps Split the implementation of `HeaderMap` into `HeaderMapImpl` so that we can write unit tests that don't depend on the `FileManager`, and then write a few tests that cover the types of corrupt header maps already detected. This also moves type and constant definitions from HeaderMap.cpp to HeaderMapTypes.h so that the test can access them. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261446 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 7dca0f623a823830c19d5d37b9ffa663deb611bf) Note: somehow unittests/Lex/HeaderMapTest.cpp needed an include of llvm/Support/MemoryBuffer.h here (but not on trunk). rdar://problem/24237557 --- include/clang/Lex/HeaderMap.h | 53 ++++++++++------- include/clang/Lex/HeaderMapTypes.h | 43 ++++++++++++++ lib/Lex/HeaderMap.cpp | 76 +++++++++--------------- unittests/Lex/CMakeLists.txt | 1 + unittests/Lex/HeaderMapTest.cpp | 95 ++++++++++++++++++++++++++++++ 5 files changed, 200 insertions(+), 68 deletions(-) create mode 100644 include/clang/Lex/HeaderMapTypes.h create mode 100644 unittests/Lex/HeaderMapTest.cpp diff --git a/include/clang/Lex/HeaderMap.h b/include/clang/Lex/HeaderMap.h index 922a272bb87..2f5178d2516 100644 --- a/include/clang/Lex/HeaderMap.h +++ b/include/clang/Lex/HeaderMap.h @@ -28,39 +28,27 @@ class FileManager; struct HMapBucket; struct HMapHeader; -/// This class represents an Apple concept known as a 'header map'. To the -/// \#include file resolution process, it basically acts like a directory of -/// symlinks to files. Its advantages are that it is dense and more efficient -/// to create and process than a directory of symlinks. -class HeaderMap { +/// Implementation for \a HeaderMap that doesn't depend on \a FileManager. +class HeaderMapImpl { std::unique_ptr FileBuffer; bool NeedsBSwap; - HeaderMap(std::unique_ptr File, bool BSwap) - : FileBuffer(std::move(File)), NeedsBSwap(BSwap) {} - public: - /// HeaderMap::Create - This attempts to load the specified file as a header - /// map. If it doesn't look like a HeaderMap, it gives up and returns null. - static const HeaderMap *Create(const FileEntry *FE, FileManager &FM); + HeaderMapImpl(std::unique_ptr File, bool NeedsBSwap) + : FileBuffer(std::move(File)), NeedsBSwap(NeedsBSwap) {} - /// LookupFile - Check to see if the specified relative filename is located in - /// this HeaderMap. If so, open it and return its FileEntry. - /// If RawPath is not NULL and the file is found, RawPath will be set to the - /// raw path at which the file was found in the file system. For example, - /// for a search path ".." and a filename "../file.h" this would be - /// "../../file.h". - const FileEntry *LookupFile(StringRef Filename, FileManager &FM) const; + // Check for a valid header and extract the byte swap. + static bool checkHeader(const llvm::MemoryBuffer &File, bool &NeedsByteSwap); /// If the specified relative filename is located in this HeaderMap return /// the filename it is mapped to, otherwise return an empty StringRef. StringRef lookupFilename(StringRef Filename, SmallVectorImpl &DestPath) const; - /// getFileName - Return the filename of the headermap. + /// Return the filename of the headermap. const char *getFileName() const; - /// dump - Print the contents of this headermap to stderr. + /// Print the contents of this headermap to stderr. void dump() const; private: @@ -70,6 +58,31 @@ class HeaderMap { const char *getString(unsigned StrTabIdx) const; }; +/// This class represents an Apple concept known as a 'header map'. To the +/// \#include file resolution process, it basically acts like a directory of +/// symlinks to files. Its advantages are that it is dense and more efficient +/// to create and process than a directory of symlinks. +class HeaderMap : private HeaderMapImpl { + HeaderMap(std::unique_ptr File, bool BSwap) + : HeaderMapImpl(std::move(File), BSwap) {} + +public: + /// This attempts to load the specified file as a header map. If it doesn't + /// look like a HeaderMap, it gives up and returns null. + static const HeaderMap *Create(const FileEntry *FE, FileManager &FM); + + /// Check to see if the specified relative filename is located in this + /// HeaderMap. If so, open it and return its FileEntry. If RawPath is not + /// NULL and the file is found, RawPath will be set to the raw path at which + /// the file was found in the file system. For example, for a search path + /// ".." and a filename "../file.h" this would be "../../file.h". + const FileEntry *LookupFile(StringRef Filename, FileManager &FM) const; + + using HeaderMapImpl::lookupFilename; + using HeaderMapImpl::getFileName; + using HeaderMapImpl::dump; +}; + } // end namespace clang. #endif diff --git a/include/clang/Lex/HeaderMapTypes.h b/include/clang/Lex/HeaderMapTypes.h new file mode 100644 index 00000000000..fbaf4baee40 --- /dev/null +++ b/include/clang/Lex/HeaderMapTypes.h @@ -0,0 +1,43 @@ +//===- HeaderMapTypes.h - Types for the header map format -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LEX_HEADERMAPTYPES_H +#define LLVM_CLANG_LEX_HEADERMAPTYPES_H + +#include + +namespace clang { + +enum { + HMAP_HeaderMagicNumber = ('h' << 24) | ('m' << 16) | ('a' << 8) | 'p', + HMAP_HeaderVersion = 1, + HMAP_EmptyBucketKey = 0 +}; + +struct HMapBucket { + uint32_t Key; // Offset (into strings) of key. + uint32_t Prefix; // Offset (into strings) of value prefix. + uint32_t Suffix; // Offset (into strings) of value suffix. +}; + +struct HMapHeader { + uint32_t Magic; // Magic word, also indicates byte order. + uint16_t Version; // Version number -- currently 1. + uint16_t Reserved; // Reserved for future use - zero for now. + uint32_t StringsOffset; // Offset to start of string pool. + uint32_t NumEntries; // Number of entries in the string table. + uint32_t NumBuckets; // Number of buckets (always a power of 2). + uint32_t MaxValueLength; // Length of longest result path (excluding nul). + // An array of 'NumBuckets' HMapBucket objects follows this header. + // Strings follow the buckets, at StringsOffset. +}; + +} // end namespace clang. + +#endif diff --git a/lib/Lex/HeaderMap.cpp b/lib/Lex/HeaderMap.cpp index 0735d386e07..26a179cf4e6 100644 --- a/lib/Lex/HeaderMap.cpp +++ b/lib/Lex/HeaderMap.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/Lex/HeaderMap.h" +#include "clang/Lex/HeaderMapTypes.h" #include "clang/Basic/CharInfo.h" #include "clang/Basic/FileManager.h" #include "llvm/ADT/SmallString.h" @@ -22,38 +23,6 @@ #include using namespace clang; -//===----------------------------------------------------------------------===// -// Data Structures and Manifest Constants -//===----------------------------------------------------------------------===// - -enum { - HMAP_HeaderMagicNumber = ('h' << 24) | ('m' << 16) | ('a' << 8) | 'p', - HMAP_HeaderVersion = 1, - - HMAP_EmptyBucketKey = 0 -}; - -namespace clang { -struct HMapBucket { - uint32_t Key; // Offset (into strings) of key. - - uint32_t Prefix; // Offset (into strings) of value prefix. - uint32_t Suffix; // Offset (into strings) of value suffix. -}; - -struct HMapHeader { - uint32_t Magic; // Magic word, also indicates byte order. - uint16_t Version; // Version number -- currently 1. - uint16_t Reserved; // Reserved for future use - zero for now. - uint32_t StringsOffset; // Offset to start of string pool. - uint32_t NumEntries; // Number of entries in the string table. - uint32_t NumBuckets; // Number of buckets (always a power of 2). - uint32_t MaxValueLength; // Length of longest result path (excluding nul). - // An array of 'NumBuckets' HMapBucket objects follows this header. - // Strings follow the buckets, at StringsOffset. -}; -} // end namespace clang. - /// HashHMapKey - This is the 'well known' hash function required by the file /// format, used to look up keys in the hash table. The hash table uses simple /// linear probing based on this function. @@ -82,15 +51,25 @@ const HeaderMap *HeaderMap::Create(const FileEntry *FE, FileManager &FM) { if (FileSize <= sizeof(HMapHeader)) return nullptr; auto FileBuffer = FM.getBufferForFile(FE); - if (!FileBuffer) return nullptr; // Unreadable file? - const char *FileStart = (*FileBuffer)->getBufferStart(); + if (!FileBuffer || !*FileBuffer) + return nullptr; + bool NeedsByteSwap; + if (!checkHeader(**FileBuffer, NeedsByteSwap)) + return nullptr; + return new HeaderMap(std::move(*FileBuffer), NeedsByteSwap); +} + +bool HeaderMapImpl::checkHeader(const llvm::MemoryBuffer &File, + bool &NeedsByteSwap) { + if (File.getBufferSize() <= sizeof(HMapHeader)) + return false; + const char *FileStart = File.getBufferStart(); // We know the file is at least as big as the header, check it now. const HMapHeader *Header = reinterpret_cast(FileStart); // Sniff it to see if it's a headermap by checking the magic number and // version. - bool NeedsByteSwap; if (Header->Magic == HMAP_HeaderMagicNumber && Header->Version == HMAP_HeaderVersion) NeedsByteSwap = false; @@ -98,12 +77,13 @@ const HeaderMap *HeaderMap::Create(const FileEntry *FE, FileManager &FM) { Header->Version == llvm::ByteSwap_16(HMAP_HeaderVersion)) NeedsByteSwap = true; // Mixed endianness headermap. else - return nullptr; // Not a header map. + return false; // Not a header map. - if (Header->Reserved != 0) return nullptr; + if (Header->Reserved != 0) + return false; - // Okay, everything looks good, create the header map. - return new HeaderMap(std::move(*FileBuffer), NeedsByteSwap); + // Okay, everything looks good. + return true; } //===----------------------------------------------------------------------===// @@ -112,18 +92,18 @@ const HeaderMap *HeaderMap::Create(const FileEntry *FE, FileManager &FM) { /// getFileName - Return the filename of the headermap. -const char *HeaderMap::getFileName() const { +const char *HeaderMapImpl::getFileName() const { return FileBuffer->getBufferIdentifier(); } -unsigned HeaderMap::getEndianAdjustedWord(unsigned X) const { +unsigned HeaderMapImpl::getEndianAdjustedWord(unsigned X) const { if (!NeedsBSwap) return X; return llvm::ByteSwap_32(X); } /// getHeader - Return a reference to the file header, in unbyte-swapped form. /// This method cannot fail. -const HMapHeader &HeaderMap::getHeader() const { +const HMapHeader &HeaderMapImpl::getHeader() const { // We know the file is at least as big as the header. Return it. return *reinterpret_cast(FileBuffer->getBufferStart()); } @@ -131,7 +111,7 @@ const HMapHeader &HeaderMap::getHeader() const { /// getBucket - Return the specified hash table bucket from the header map, /// bswap'ing its fields as appropriate. If the bucket number is not valid, /// this return a bucket with an empty key (0). -HMapBucket HeaderMap::getBucket(unsigned BucketNo) const { +HMapBucket HeaderMapImpl::getBucket(unsigned BucketNo) const { HMapBucket Result; Result.Key = HMAP_EmptyBucketKey; @@ -155,7 +135,7 @@ HMapBucket HeaderMap::getBucket(unsigned BucketNo) const { /// getString - Look up the specified string in the string table. If the string /// index is not valid, it returns an empty string. -const char *HeaderMap::getString(unsigned StrTabIdx) const { +const char *HeaderMapImpl::getString(unsigned StrTabIdx) const { // Add the start of the string table to the idx. StrTabIdx += getEndianAdjustedWord(getHeader().StringsOffset); @@ -174,7 +154,7 @@ const char *HeaderMap::getString(unsigned StrTabIdx) const { //===----------------------------------------------------------------------===// /// dump - Print the contents of this headermap to stderr. -LLVM_DUMP_METHOD void HeaderMap::dump() const { +LLVM_DUMP_METHOD void HeaderMapImpl::dump() const { const HMapHeader &Hdr = getHeader(); unsigned NumBuckets = getEndianAdjustedWord(Hdr.NumBuckets); @@ -199,15 +179,15 @@ const FileEntry *HeaderMap::LookupFile( StringRef Filename, FileManager &FM) const { SmallString<1024> Path; - StringRef Dest = lookupFilename(Filename, Path); + StringRef Dest = HeaderMapImpl::lookupFilename(Filename, Path); if (Dest.empty()) return nullptr; return FM.getFile(Dest); } -StringRef HeaderMap::lookupFilename(StringRef Filename, - SmallVectorImpl &DestPath) const { +StringRef HeaderMapImpl::lookupFilename(StringRef Filename, + SmallVectorImpl &DestPath) const { const HMapHeader &Hdr = getHeader(); unsigned NumBuckets = getEndianAdjustedWord(Hdr.NumBuckets); diff --git a/unittests/Lex/CMakeLists.txt b/unittests/Lex/CMakeLists.txt index 68730b40d7c..2002fbf5eac 100644 --- a/unittests/Lex/CMakeLists.txt +++ b/unittests/Lex/CMakeLists.txt @@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS ) add_clang_unittest(LexTests + HeaderMapTest.cpp LexerTest.cpp PPCallbacksTest.cpp PPConditionalDirectiveRecordTest.cpp diff --git a/unittests/Lex/HeaderMapTest.cpp b/unittests/Lex/HeaderMapTest.cpp new file mode 100644 index 00000000000..b9ed437c3f7 --- /dev/null +++ b/unittests/Lex/HeaderMapTest.cpp @@ -0,0 +1,95 @@ +//===- unittests/Lex/HeaderMapTest.cpp - HeaderMap tests ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===--------------------------------------------------------------===// + +#include "clang/Lex/HeaderMap.h" +#include "clang/Lex/HeaderMapTypes.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SwapByteOrder.h" +#include "gtest/gtest.h" + +using namespace clang; +using namespace llvm; + +namespace { + +// Lay out a header file for testing. +template struct MapFile { + HMapHeader Header; + HMapBucket Buckets[NumBuckets]; + unsigned char Bytes[NumBytes]; + + void init() { + memset(this, 0, sizeof(MapFile)); + Header.Magic = HMAP_HeaderMagicNumber; + Header.Version = HMAP_HeaderVersion; + Header.NumBuckets = NumBuckets; + Header.StringsOffset = sizeof(Header) + sizeof(Buckets); + } + + void swapBytes() { + using llvm::sys::getSwappedBytes; + Header.Magic = getSwappedBytes(Header.Magic); + Header.Version = getSwappedBytes(Header.Version); + Header.NumBuckets = getSwappedBytes(Header.NumBuckets); + Header.StringsOffset = getSwappedBytes(Header.StringsOffset); + } + + std::unique_ptr getBuffer() const { + return MemoryBuffer::getMemBuffer( + StringRef(reinterpret_cast(this), sizeof(MapFile)), + "header", + /* RequresNullTerminator */ false); + } +}; + +TEST(HeaderMapTest, checkHeaderEmpty) { + bool NeedsSwap; + ASSERT_FALSE(HeaderMapImpl::checkHeader( + *MemoryBuffer::getMemBufferCopy("", "empty"), NeedsSwap)); + ASSERT_FALSE(HeaderMapImpl::checkHeader( + *MemoryBuffer::getMemBufferCopy("", "empty"), NeedsSwap)); +} + +TEST(HeaderMapTest, checkHeaderMagic) { + MapFile<1, 1> File; + File.init(); + File.Header.Magic = 0; + bool NeedsSwap; + ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); +} + +TEST(HeaderMapTest, checkHeaderReserved) { + MapFile<1, 1> File; + File.init(); + File.Header.Reserved = 1; + bool NeedsSwap; + ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); +} + +TEST(HeaderMapTest, checkHeaderVersion) { + MapFile<1, 1> File; + File.init(); + ++File.Header.Version; + bool NeedsSwap; + ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); +} + +TEST(HeaderMapTest, checkHeaderValidButEmpty) { + MapFile<1, 1> File; + File.init(); + bool NeedsSwap; + ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); + ASSERT_FALSE(NeedsSwap); + + File.swapBytes(); + ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); + ASSERT_TRUE(NeedsSwap); +} + +} // end namespace From 5889d42ec53f29c4c1f931177c84b0855722b80e Mon Sep 17 00:00:00 2001 From: "Duncan P. N. Exon Smith" Date: Sat, 20 Feb 2016 21:00:58 +0000 Subject: [PATCH 201/742] Lex: Check buckets on header map construction If the number of buckets is not a power of two, immediately recognize the header map as corrupt, rather than waiting for the first lookup. I converted the later check to an assert. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261448 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 4a8b8ff576265eb20d5dda28e7cb4ef8d0da1159) --- lib/Lex/HeaderMap.cpp | 16 ++++++++++++---- unittests/Lex/HeaderMapTest.cpp | 9 +++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/lib/Lex/HeaderMap.cpp b/lib/Lex/HeaderMap.cpp index 26a179cf4e6..2b31ab9d044 100644 --- a/lib/Lex/HeaderMap.cpp +++ b/lib/Lex/HeaderMap.cpp @@ -19,6 +19,7 @@ #include "llvm/Support/DataTypes.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SwapByteOrder.h" #include #include using namespace clang; @@ -82,6 +83,15 @@ bool HeaderMapImpl::checkHeader(const llvm::MemoryBuffer &File, if (Header->Reserved != 0) return false; + // Check the number of buckets. + auto NumBuckets = NeedsByteSwap + ? llvm::sys::getSwappedBytes(Header->NumBuckets) + : Header->NumBuckets; + + // If the number of buckets is not a power of two, the headermap is corrupt. + if (NumBuckets & (NumBuckets - 1)) + return false; + // Okay, everything looks good. return true; } @@ -191,10 +201,8 @@ StringRef HeaderMapImpl::lookupFilename(StringRef Filename, const HMapHeader &Hdr = getHeader(); unsigned NumBuckets = getEndianAdjustedWord(Hdr.NumBuckets); - // If the number of buckets is not a power of two, the headermap is corrupt. - // Don't probe infinitely. - if (NumBuckets & (NumBuckets-1)) - return StringRef(); + // Don't probe infinitely. This should be checked before constructing. + assert(!(NumBuckets & (NumBuckets - 1)) && "Expected power of 2"); // Linearly probe the hash table. for (unsigned Bucket = HashHMapKey(Filename);; ++Bucket) { diff --git a/unittests/Lex/HeaderMapTest.cpp b/unittests/Lex/HeaderMapTest.cpp index b9ed437c3f7..573e6701149 100644 --- a/unittests/Lex/HeaderMapTest.cpp +++ b/unittests/Lex/HeaderMapTest.cpp @@ -92,4 +92,13 @@ TEST(HeaderMapTest, checkHeaderValidButEmpty) { ASSERT_TRUE(NeedsSwap); } +TEST(HeaderMapTest, checkHeader3Buckets) { + MapFile<3, 1> File; + ASSERT_EQ(3 * sizeof(HMapBucket), sizeof(File.Buckets)); + + File.init(); + bool NeedsSwap; + ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); +} + } // end namespace From 802d1ee8ff3b1fbb85593cdb3e895dc52b378e69 Mon Sep 17 00:00:00 2001 From: "Duncan P. N. Exon Smith" Date: Sat, 20 Feb 2016 21:24:31 +0000 Subject: [PATCH 202/742] Lex: Check whether the header map buffer has space for the buckets Check up front whether the header map buffer has space for all of its declared buckets. There was already a check in `getBucket()`, but it had UB (comparing pointers that were outside of objects in the error path) and was insufficient (only checking for a single byte of the relevant bucket). I fixed the check, moved it to `checkHeader()`, and left a fixed version behind as an assertion. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261449 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 39c202cadc520b275dd3952573147dff214ea9f2) --- lib/Lex/HeaderMap.cpp | 20 ++++++++++---------- unittests/Lex/HeaderMapTest.cpp | 8 ++++++++ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/lib/Lex/HeaderMap.cpp b/lib/Lex/HeaderMap.cpp index 2b31ab9d044..220e70d8d58 100644 --- a/lib/Lex/HeaderMap.cpp +++ b/lib/Lex/HeaderMap.cpp @@ -83,14 +83,16 @@ bool HeaderMapImpl::checkHeader(const llvm::MemoryBuffer &File, if (Header->Reserved != 0) return false; - // Check the number of buckets. + // Check the number of buckets. It should be a power of two, and there + // should be enough space in the file for all of them. auto NumBuckets = NeedsByteSwap ? llvm::sys::getSwappedBytes(Header->NumBuckets) : Header->NumBuckets; - - // If the number of buckets is not a power of two, the headermap is corrupt. if (NumBuckets & (NumBuckets - 1)) return false; + if (File.getBufferSize() < + sizeof(HMapHeader) + sizeof(HMapBucket) * NumBuckets) + return false; // Okay, everything looks good. return true; @@ -122,21 +124,19 @@ const HMapHeader &HeaderMapImpl::getHeader() const { /// bswap'ing its fields as appropriate. If the bucket number is not valid, /// this return a bucket with an empty key (0). HMapBucket HeaderMapImpl::getBucket(unsigned BucketNo) const { + assert(FileBuffer->getBufferSize() >= + sizeof(HMapHeader) + sizeof(HMapBucket) * BucketNo && + "Expected bucket to be in range"); + HMapBucket Result; Result.Key = HMAP_EmptyBucketKey; const HMapBucket *BucketArray = reinterpret_cast(FileBuffer->getBufferStart() + sizeof(HMapHeader)); - const HMapBucket *BucketPtr = BucketArray+BucketNo; - if ((const char*)(BucketPtr+1) > FileBuffer->getBufferEnd()) { - Result.Prefix = 0; - Result.Suffix = 0; - return Result; // Invalid buffer, corrupt hmap. - } - // Otherwise, the bucket is valid. Load the values, bswapping as needed. + // Load the values, bswapping as needed. Result.Key = getEndianAdjustedWord(BucketPtr->Key); Result.Prefix = getEndianAdjustedWord(BucketPtr->Prefix); Result.Suffix = getEndianAdjustedWord(BucketPtr->Suffix); diff --git a/unittests/Lex/HeaderMapTest.cpp b/unittests/Lex/HeaderMapTest.cpp index 573e6701149..96e36d198de 100644 --- a/unittests/Lex/HeaderMapTest.cpp +++ b/unittests/Lex/HeaderMapTest.cpp @@ -101,4 +101,12 @@ TEST(HeaderMapTest, checkHeader3Buckets) { ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); } +TEST(HeaderMapTest, checkHeaderNotEnoughBuckets) { + MapFile<1, 1> File; + File.init(); + File.Header.NumBuckets = 8; + bool NeedsSwap; + ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); +} + } // end namespace From e17fef03cf54c9118c32a38d9ec36899f5265cfa Mon Sep 17 00:00:00 2001 From: "Duncan P. N. Exon Smith" Date: Sat, 20 Feb 2016 22:53:22 +0000 Subject: [PATCH 203/742] Lex: Add a test for HeaderMap::lookupFileName() Add a simple test for `HeaderMap::lookupFileName()`. I'm planning to add better error checking in a moment, and I'll add more tests like this then. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261455 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 5900ac9ac63bacb65273c32e964e087e2ed98337) --- unittests/Lex/HeaderMapTest.cpp | 62 +++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/unittests/Lex/HeaderMapTest.cpp b/unittests/Lex/HeaderMapTest.cpp index 96e36d198de..12d0865e27a 100644 --- a/unittests/Lex/HeaderMapTest.cpp +++ b/unittests/Lex/HeaderMapTest.cpp @@ -7,11 +7,14 @@ // //===--------------------------------------------------------------===// +#include "clang/Basic/CharInfo.h" #include "clang/Lex/HeaderMap.h" #include "clang/Lex/HeaderMapTypes.h" +#include "llvm/ADT/SmallString.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SwapByteOrder.h" #include "gtest/gtest.h" +#include using namespace clang; using namespace llvm; @@ -48,6 +51,45 @@ template struct MapFile { } }; +// The header map hash function. +static inline unsigned getHash(StringRef Str) { + unsigned Result = 0; + for (char C : Str) + Result += toLowercase(C) * 13; + return Result; +} + +template struct FileMaker { + FileTy &File; + unsigned SI = 1; + unsigned BI = 0; + FileMaker(FileTy &File) : File(File) {} + + unsigned addString(StringRef S) { + assert(SI + S.size() + 1 <= sizeof(File.Bytes)); + std::copy(S.begin(), S.end(), File.Bytes + SI); + auto OldSI = SI; + SI += S.size() + 1; + return OldSI; + } + void addBucket(unsigned Hash, unsigned Key, unsigned Prefix, unsigned Suffix) { + assert(!(File.Header.NumBuckets & (File.Header.NumBuckets - 1))); + unsigned I = Hash & (File.Header.NumBuckets - 1); + do { + if (!File.Buckets[I].Key) { + File.Buckets[I].Key = Key; + File.Buckets[I].Prefix = Prefix; + File.Buckets[I].Suffix = Suffix; + ++File.Header.NumEntries; + return; + } + ++I; + I &= File.Header.NumBuckets - 1; + } while (I != (Hash & (File.Header.NumBuckets - 1))); + llvm_unreachable("no empty buckets"); + } +}; + TEST(HeaderMapTest, checkHeaderEmpty) { bool NeedsSwap; ASSERT_FALSE(HeaderMapImpl::checkHeader( @@ -109,4 +151,24 @@ TEST(HeaderMapTest, checkHeaderNotEnoughBuckets) { ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); } +TEST(HeaderMapTest, lookupFilename) { + typedef MapFile<2, 7> FileTy; + FileTy File; + File.init(); + + FileMaker Maker(File); + auto a = Maker.addString("a"); + auto b = Maker.addString("b"); + auto c = Maker.addString("c"); + Maker.addBucket(getHash("a"), a, b, c); + + bool NeedsSwap; + ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); + ASSERT_FALSE(NeedsSwap); + HeaderMapImpl Map(File.getBuffer(), NeedsSwap); + + SmallString<8> DestPath; + ASSERT_EQ("bc", Map.lookupFilename("a", DestPath)); +} + } // end namespace From e582e4b6d5f5a2e3c429d8c172fe5d93d1d0cd7c Mon Sep 17 00:00:00 2001 From: "Duncan P. N. Exon Smith" Date: Sat, 20 Feb 2016 23:09:14 +0000 Subject: [PATCH 204/742] Lex: Use dbgs() instead of fprintf() in HeaderMap::dump() This way it's easy to change HeaderMapImpl::getString() to return a StringRef. There's a slight change here, because I used `errs()` instead of `dbgs()`. But `dbgs()` is more appropriate for a dump method. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261456 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit c41b90a59cc79231b5ac5772309313a54343d94b) --- lib/Lex/HeaderMap.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/Lex/HeaderMap.cpp b/lib/Lex/HeaderMap.cpp index 220e70d8d58..394a51dbb70 100644 --- a/lib/Lex/HeaderMap.cpp +++ b/lib/Lex/HeaderMap.cpp @@ -20,7 +20,7 @@ #include "llvm/Support/MathExtras.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SwapByteOrder.h" -#include +#include "llvm/Support/Debug.h" #include using namespace clang; @@ -168,9 +168,8 @@ LLVM_DUMP_METHOD void HeaderMapImpl::dump() const { const HMapHeader &Hdr = getHeader(); unsigned NumBuckets = getEndianAdjustedWord(Hdr.NumBuckets); - fprintf(stderr, "Header Map %s:\n %d buckets, %d entries\n", - getFileName(), NumBuckets, - getEndianAdjustedWord(Hdr.NumEntries)); + llvm::dbgs() << "Header Map " << getFileName() << ":\n " << NumBuckets + << ", " << getEndianAdjustedWord(Hdr.NumEntries) << "\n"; for (unsigned i = 0; i != NumBuckets; ++i) { HMapBucket B = getBucket(i); @@ -179,7 +178,8 @@ LLVM_DUMP_METHOD void HeaderMapImpl::dump() const { const char *Key = getString(B.Key); const char *Prefix = getString(B.Prefix); const char *Suffix = getString(B.Suffix); - fprintf(stderr, " %d. %s -> '%s' '%s'\n", i, Key, Prefix, Suffix); + llvm::dbgs() << " " << i << ". " << Key << " -> '" << Prefix << "' '" + << Suffix << "'\n"; } } From 3d5f124bbb9e5e2a5cee8224579919e70a67dc4d Mon Sep 17 00:00:00 2001 From: "Duncan P. N. Exon Smith" Date: Sat, 20 Feb 2016 23:12:51 +0000 Subject: [PATCH 205/742] Lex: Change HeaderMapImpl::getString() to return StringRef, NFC git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261459 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 4e8d52ad038b2ca4b8a702495da54d594298fc34) --- include/clang/Lex/HeaderMap.h | 2 +- lib/Lex/HeaderMap.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/clang/Lex/HeaderMap.h b/include/clang/Lex/HeaderMap.h index 2f5178d2516..da409feece7 100644 --- a/include/clang/Lex/HeaderMap.h +++ b/include/clang/Lex/HeaderMap.h @@ -55,7 +55,7 @@ class HeaderMapImpl { unsigned getEndianAdjustedWord(unsigned X) const; const HMapHeader &getHeader() const; HMapBucket getBucket(unsigned BucketNo) const; - const char *getString(unsigned StrTabIdx) const; + StringRef getString(unsigned StrTabIdx) const; }; /// This class represents an Apple concept known as a 'header map'. To the diff --git a/lib/Lex/HeaderMap.cpp b/lib/Lex/HeaderMap.cpp index 394a51dbb70..afa2631ac5b 100644 --- a/lib/Lex/HeaderMap.cpp +++ b/lib/Lex/HeaderMap.cpp @@ -145,7 +145,7 @@ HMapBucket HeaderMapImpl::getBucket(unsigned BucketNo) const { /// getString - Look up the specified string in the string table. If the string /// index is not valid, it returns an empty string. -const char *HeaderMapImpl::getString(unsigned StrTabIdx) const { +StringRef HeaderMapImpl::getString(unsigned StrTabIdx) const { // Add the start of the string table to the idx. StrTabIdx += getEndianAdjustedWord(getHeader().StringsOffset); @@ -175,9 +175,9 @@ LLVM_DUMP_METHOD void HeaderMapImpl::dump() const { HMapBucket B = getBucket(i); if (B.Key == HMAP_EmptyBucketKey) continue; - const char *Key = getString(B.Key); - const char *Prefix = getString(B.Prefix); - const char *Suffix = getString(B.Suffix); + StringRef Key = getString(B.Key); + StringRef Prefix = getString(B.Prefix); + StringRef Suffix = getString(B.Suffix); llvm::dbgs() << " " << i << ". " << Key << " -> '" << Prefix << "' '" << Suffix << "'\n"; } From 00aa137b08e5265b82c365101d07bec382c6167c Mon Sep 17 00:00:00 2001 From: "Duncan P. N. Exon Smith" Date: Sun, 21 Feb 2016 00:14:36 +0000 Subject: [PATCH 206/742] Lex: Never overflow the file in HeaderMap::lookupFilename() If a header map file is corrupt, the strings in the string table may not be null-terminated. The logic here previously relied on `MemoryBuffer` always being null-terminated, but this isn't actually guaranteed by the class AFAICT. Moreover, we're seeing a lot of crash traces at calls to `strlen()` inside of `lookupFilename()`, so something is going wrong there. Instead, use `strnlen()` to get the length, and check for corruption. Also remove code paths that could call `StringRef(nullptr)`. r261459 made these rather obvious (although they'd been there all along). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261461 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 839cd13b65d802f06276ff88d234419c38a44199) --- lib/Lex/HeaderMap.cpp | 16 +++++++++---- unittests/Lex/HeaderMapTest.cpp | 42 +++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/lib/Lex/HeaderMap.cpp b/lib/Lex/HeaderMap.cpp index afa2631ac5b..67b663158f0 100644 --- a/lib/Lex/HeaderMap.cpp +++ b/lib/Lex/HeaderMap.cpp @@ -21,6 +21,7 @@ #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SwapByteOrder.h" #include "llvm/Support/Debug.h" +#include #include using namespace clang; @@ -151,12 +152,17 @@ StringRef HeaderMapImpl::getString(unsigned StrTabIdx) const { // Check for invalid index. if (StrTabIdx >= FileBuffer->getBufferSize()) - return nullptr; + return ""; + + const char *Data = FileBuffer->getBufferStart() + StrTabIdx; + unsigned MaxLen = FileBuffer->getBufferSize() - StrTabIdx; + unsigned Len = strnlen(Data, MaxLen); + + // Check whether the buffer is null-terminated. + if (Len == MaxLen && Data[Len - 1]) + return ""; - // Otherwise, we have a valid pointer into the file. Just return it. We know - // that the "string" can not overrun the end of the file, because the buffer - // is nul terminated by virtue of being a MemoryBuffer. - return FileBuffer->getBufferStart()+StrTabIdx; + return StringRef(Data, Len); } //===----------------------------------------------------------------------===// diff --git a/unittests/Lex/HeaderMapTest.cpp b/unittests/Lex/HeaderMapTest.cpp index 12d0865e27a..e0e97937d6c 100644 --- a/unittests/Lex/HeaderMapTest.cpp +++ b/unittests/Lex/HeaderMapTest.cpp @@ -15,6 +15,7 @@ #include "llvm/Support/SwapByteOrder.h" #include "gtest/gtest.h" #include +#include using namespace clang; using namespace llvm; @@ -171,4 +172,45 @@ TEST(HeaderMapTest, lookupFilename) { ASSERT_EQ("bc", Map.lookupFilename("a", DestPath)); } +template struct PaddedFile { + FileTy File; + PaddingTy Padding; +}; + +TEST(HeaderMapTest, lookupFilenameTruncated) { + typedef MapFile<2, 64 - sizeof(HMapHeader) - 2 * sizeof(HMapBucket)> FileTy; + static_assert(std::is_standard_layout::value, + "Expected standard layout"); + static_assert(sizeof(FileTy) == 64, "check the math"); + PaddedFile P; + auto &File = P.File; + auto &Padding = P.Padding; + File.init(); + + FileMaker Maker(File); + auto a = Maker.addString("a"); + auto b = Maker.addString("b"); + auto c = Maker.addString("c"); + Maker.addBucket(getHash("a"), a, b, c); + + // Add 'x' characters to cause an overflow into Padding. + ASSERT_EQ('c', File.Bytes[5]); + for (unsigned I = 6; I < sizeof(File.Bytes); ++I) { + ASSERT_EQ(0, File.Bytes[I]); + File.Bytes[I] = 'x'; + } + Padding = 0xffffffff; // Padding won't stop it either. + + bool NeedsSwap; + ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); + ASSERT_FALSE(NeedsSwap); + HeaderMapImpl Map(File.getBuffer(), NeedsSwap); + + // The string for "c" runs to the end of File. Check that the suffix + // ("cxxxx...") is ignored. Another option would be to return an empty + // filename altogether. + SmallString<24> DestPath; + ASSERT_EQ("b", Map.lookupFilename("a", DestPath)); +} + } // end namespace From b70160ad3c5c029f68dfb7ce6b8897fb7d156209 Mon Sep 17 00:00:00 2001 From: "Duncan P. N. Exon Smith" Date: Mon, 22 Feb 2016 22:24:22 +0000 Subject: [PATCH 207/742] Lex: Check for 0 buckets on header map construction Switch to using `isPowerOf2_32()` to check whether the buckets are a power of two, and as a side benefit reject loading a header map with no buckets. This is a follow-up to r261448. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261585 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 006186b25e2e8fc902211cc08bea285c38cc42f2) --- lib/Lex/HeaderMap.cpp | 10 +++++----- unittests/Lex/HeaderMapTest.cpp | 9 +++++++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/Lex/HeaderMap.cpp b/lib/Lex/HeaderMap.cpp index 67b663158f0..be0d477e8b5 100644 --- a/lib/Lex/HeaderMap.cpp +++ b/lib/Lex/HeaderMap.cpp @@ -86,10 +86,10 @@ bool HeaderMapImpl::checkHeader(const llvm::MemoryBuffer &File, // Check the number of buckets. It should be a power of two, and there // should be enough space in the file for all of them. - auto NumBuckets = NeedsByteSwap - ? llvm::sys::getSwappedBytes(Header->NumBuckets) - : Header->NumBuckets; - if (NumBuckets & (NumBuckets - 1)) + uint32_t NumBuckets = NeedsByteSwap + ? llvm::sys::getSwappedBytes(Header->NumBuckets) + : Header->NumBuckets; + if (!llvm::isPowerOf2_32(NumBuckets)) return false; if (File.getBufferSize() < sizeof(HMapHeader) + sizeof(HMapBucket) * NumBuckets) @@ -208,7 +208,7 @@ StringRef HeaderMapImpl::lookupFilename(StringRef Filename, unsigned NumBuckets = getEndianAdjustedWord(Hdr.NumBuckets); // Don't probe infinitely. This should be checked before constructing. - assert(!(NumBuckets & (NumBuckets - 1)) && "Expected power of 2"); + assert(llvm::isPowerOf2_32(NumBuckets) && "Expected power of 2"); // Linearly probe the hash table. for (unsigned Bucket = HashHMapKey(Filename);; ++Bucket) { diff --git a/unittests/Lex/HeaderMapTest.cpp b/unittests/Lex/HeaderMapTest.cpp index e0e97937d6c..314078390f4 100644 --- a/unittests/Lex/HeaderMapTest.cpp +++ b/unittests/Lex/HeaderMapTest.cpp @@ -144,6 +144,15 @@ TEST(HeaderMapTest, checkHeader3Buckets) { ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); } +TEST(HeaderMapTest, checkHeader0Buckets) { + // Create with 1 bucket to avoid 0-sized arrays. + MapFile<1, 1> File; + File.init(); + File.Header.NumBuckets = 0; + bool NeedsSwap; + ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); +} + TEST(HeaderMapTest, checkHeaderNotEnoughBuckets) { MapFile<1, 1> File; File.init(); From 04e3d9442e18aa5d5c1d0587672022e70d7a2e29 Mon Sep 17 00:00:00 2001 From: "Duncan P. N. Exon Smith" Date: Tue, 23 Feb 2016 00:48:16 +0000 Subject: [PATCH 208/742] Lex: Return "" when HeaderMap::lookupFilename fails Change getString() to return Optional, and change lookupFilename() to return an empty string if either one of the prefix and suffix can't be found. This is a more robust follow-up to r261461, but it's still not entirely satisfactory. Ideally we'd report that the header map is corrupt; perhaps something for a follow-up. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261596 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 6d0c65dab43962dff862d8263333feb385d71c96) --- include/clang/Lex/HeaderMap.h | 6 ++++- lib/Lex/HeaderMap.cpp | 37 +++++++++++++++++++---------- unittests/Lex/HeaderMapTest.cpp | 42 +++++++++++++++++++++++++++++---- 3 files changed, 67 insertions(+), 18 deletions(-) diff --git a/include/clang/Lex/HeaderMap.h b/include/clang/Lex/HeaderMap.h index da409feece7..58cd549d744 100644 --- a/include/clang/Lex/HeaderMap.h +++ b/include/clang/Lex/HeaderMap.h @@ -15,6 +15,7 @@ #define LLVM_CLANG_LEX_HEADERMAP_H #include "clang/Basic/LLVM.h" +#include "llvm/ADT/Optional.h" #include "llvm/Support/Compiler.h" #include @@ -55,7 +56,10 @@ class HeaderMapImpl { unsigned getEndianAdjustedWord(unsigned X) const; const HMapHeader &getHeader() const; HMapBucket getBucket(unsigned BucketNo) const; - StringRef getString(unsigned StrTabIdx) const; + + /// Look up the specified string in the string table. If the string index is + /// not valid, return None. + Optional getString(unsigned StrTabIdx) const; }; /// This class represents an Apple concept known as a 'header map'. To the diff --git a/lib/Lex/HeaderMap.cpp b/lib/Lex/HeaderMap.cpp index be0d477e8b5..4cace5b0024 100644 --- a/lib/Lex/HeaderMap.cpp +++ b/lib/Lex/HeaderMap.cpp @@ -16,6 +16,7 @@ #include "clang/Basic/CharInfo.h" #include "clang/Basic/FileManager.h" #include "llvm/ADT/SmallString.h" +#include "llvm/Support/Compiler.h" #include "llvm/Support/DataTypes.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/MemoryBuffer.h" @@ -144,15 +145,13 @@ HMapBucket HeaderMapImpl::getBucket(unsigned BucketNo) const { return Result; } -/// getString - Look up the specified string in the string table. If the string -/// index is not valid, it returns an empty string. -StringRef HeaderMapImpl::getString(unsigned StrTabIdx) const { +Optional HeaderMapImpl::getString(unsigned StrTabIdx) const { // Add the start of the string table to the idx. StrTabIdx += getEndianAdjustedWord(getHeader().StringsOffset); // Check for invalid index. if (StrTabIdx >= FileBuffer->getBufferSize()) - return ""; + return None; const char *Data = FileBuffer->getBufferStart() + StrTabIdx; unsigned MaxLen = FileBuffer->getBufferSize() - StrTabIdx; @@ -160,7 +159,7 @@ StringRef HeaderMapImpl::getString(unsigned StrTabIdx) const { // Check whether the buffer is null-terminated. if (Len == MaxLen && Data[Len - 1]) - return ""; + return None; return StringRef(Data, Len); } @@ -177,13 +176,19 @@ LLVM_DUMP_METHOD void HeaderMapImpl::dump() const { llvm::dbgs() << "Header Map " << getFileName() << ":\n " << NumBuckets << ", " << getEndianAdjustedWord(Hdr.NumEntries) << "\n"; + auto getStringOrInvalid = [this](unsigned Id) -> StringRef { + if (Optional S = getString(Id)) + return *S; + return ""; + }; + for (unsigned i = 0; i != NumBuckets; ++i) { HMapBucket B = getBucket(i); if (B.Key == HMAP_EmptyBucketKey) continue; - StringRef Key = getString(B.Key); - StringRef Prefix = getString(B.Prefix); - StringRef Suffix = getString(B.Suffix); + StringRef Key = getStringOrInvalid(B.Key); + StringRef Prefix = getStringOrInvalid(B.Prefix); + StringRef Suffix = getStringOrInvalid(B.Suffix); llvm::dbgs() << " " << i << ". " << Key << " -> '" << Prefix << "' '" << Suffix << "'\n"; } @@ -216,16 +221,22 @@ StringRef HeaderMapImpl::lookupFilename(StringRef Filename, if (B.Key == HMAP_EmptyBucketKey) return StringRef(); // Hash miss. // See if the key matches. If not, probe on. - if (!Filename.equals_lower(getString(B.Key))) + Optional Key = getString(B.Key); + if (LLVM_UNLIKELY(!Key)) + continue; + if (!Filename.equals_lower(*Key)) continue; // If so, we have a match in the hash table. Construct the destination // path. - StringRef Prefix = getString(B.Prefix); - StringRef Suffix = getString(B.Suffix); + Optional Prefix = getString(B.Prefix); + Optional Suffix = getString(B.Suffix); + DestPath.clear(); - DestPath.append(Prefix.begin(), Prefix.end()); - DestPath.append(Suffix.begin(), Suffix.end()); + if (LLVM_LIKELY(Prefix && Suffix)) { + DestPath.append(Prefix->begin(), Prefix->end()); + DestPath.append(Suffix->begin(), Suffix->end()); + } return StringRef(DestPath.begin(), DestPath.size()); } } diff --git a/unittests/Lex/HeaderMapTest.cpp b/unittests/Lex/HeaderMapTest.cpp index 314078390f4..742c9aa75b0 100644 --- a/unittests/Lex/HeaderMapTest.cpp +++ b/unittests/Lex/HeaderMapTest.cpp @@ -186,7 +186,7 @@ template struct PaddedFile { PaddingTy Padding; }; -TEST(HeaderMapTest, lookupFilenameTruncated) { +TEST(HeaderMapTest, lookupFilenameTruncatedSuffix) { typedef MapFile<2, 64 - sizeof(HMapHeader) - 2 * sizeof(HMapBucket)> FileTy; static_assert(std::is_standard_layout::value, "Expected standard layout"); @@ -216,10 +216,44 @@ TEST(HeaderMapTest, lookupFilenameTruncated) { HeaderMapImpl Map(File.getBuffer(), NeedsSwap); // The string for "c" runs to the end of File. Check that the suffix - // ("cxxxx...") is ignored. Another option would be to return an empty - // filename altogether. + // ("cxxxx...") is detected as truncated, and an empty string is returned. SmallString<24> DestPath; - ASSERT_EQ("b", Map.lookupFilename("a", DestPath)); + ASSERT_EQ("", Map.lookupFilename("a", DestPath)); +} + +TEST(HeaderMapTest, lookupFilenameTruncatedPrefix) { + typedef MapFile<2, 64 - sizeof(HMapHeader) - 2 * sizeof(HMapBucket)> FileTy; + static_assert(std::is_standard_layout::value, + "Expected standard layout"); + static_assert(sizeof(FileTy) == 64, "check the math"); + PaddedFile P; + auto &File = P.File; + auto &Padding = P.Padding; + File.init(); + + FileMaker Maker(File); + auto a = Maker.addString("a"); + auto c = Maker.addString("c"); + auto b = Maker.addString("b"); // Store the prefix last. + Maker.addBucket(getHash("a"), a, b, c); + + // Add 'x' characters to cause an overflow into Padding. + ASSERT_EQ('b', File.Bytes[5]); + for (unsigned I = 6; I < sizeof(File.Bytes); ++I) { + ASSERT_EQ(0, File.Bytes[I]); + File.Bytes[I] = 'x'; + } + Padding = 0xffffffff; // Padding won't stop it either. + + bool NeedsSwap; + ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); + ASSERT_FALSE(NeedsSwap); + HeaderMapImpl Map(File.getBuffer(), NeedsSwap); + + // The string for "b" runs to the end of File. Check that the prefix + // ("bxxxx...") is detected as truncated, and an empty string is returned. + SmallString<24> DestPath; + ASSERT_EQ("", Map.lookupFilename("a", DestPath)); } } // end namespace From f953dddad01770bb89f9fadb809ed6842f6aa5ba Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Tue, 23 Feb 2016 09:54:41 -0800 Subject: [PATCH 209/742] Remove an unnecessary workaround introduced in r259975. (NFC) Now that LLVM r259973 allows replacing a temporary type with another temporary we can rely on the original implementation. It is possible for enums to be created as part of their own declcontext. In this case a FwdDecl will be created twice. This doesn't cause a problem because both FwdDecls are entered into the ReplaceMap: finalize() will replace the first FwdDecl with the second and then replace the second with complete type. Thanks to echristo for pointing this out. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261657 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit c473d6dc331e6e199803d742e3bdd28035191fd0) # Conflicts: # lib/CodeGen/CGDebugInfo.cpp --- lib/CodeGen/CGDebugInfo.cpp | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index 52bd805b81c..e300ed538ac 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -2051,24 +2051,22 @@ llvm::DIType *CGDebugInfo::CreateEnumType(const EnumType *Ty) { // If this is just a forward declaration, construct an appropriately // marked node and just return it. if (isImportedFromModule || !ED->getDefinition()) { + // Note that it is possible for enums to be created as part of + // their own declcontext. In this case a FwdDecl will be created + // twice. This doesn't cause a problem because both FwdDecls are + // entered into the ReplaceMap: finalize() will replace the first + // FwdDecl with the second and then replace the second with + // complete type. + llvm::DIScope *EDContext = getDeclContextDescriptor(ED); llvm::DIFile *DefUnit = getOrCreateFile(ED->getLocation()); - - // It is possible for enums to be created as part of their own - // declcontext. We need to cache a placeholder to avoid the type being - // created twice before hitting the cache. llvm::TempDIScope TmpContext(DBuilder.createReplaceableCompositeType( llvm::dwarf::DW_TAG_enumeration_type, "", TheCU, DefUnit, 0)); unsigned Line = getLineNumber(ED->getLocation()); StringRef EDName = ED->getName(); llvm::DIType *RetTy = DBuilder.createReplaceableCompositeType( - llvm::dwarf::DW_TAG_enumeration_type, EDName, TmpContext.get(), DefUnit, - Line, 0, Size, Align, llvm::DINode::FlagFwdDecl, FullName); - - // Cache the enum type so it is available when building the declcontext - // and replace the declcontect with the real thing. - TypeCache[Ty].reset(RetTy); - TmpContext->replaceAllUsesWith(getDeclContextDescriptor(ED)); + llvm::dwarf::DW_TAG_enumeration_type, EDName, EDContext, DefUnit, Line, + 0, Size, Align, llvm::DINode::FlagFwdDecl, FullName); ReplaceMap.emplace_back( std::piecewise_construct, std::make_tuple(Ty), From 6e9af5bbd69d2686a2257f280a82bdba977c6f1b Mon Sep 17 00:00:00 2001 From: Chris Bieneman Date: Tue, 23 Feb 2016 20:33:15 +0000 Subject: [PATCH 210/742] [CMake] Add install-clang-format target by migrating to add_clang_tool This change migrates clang-format to add_clang_tool which makes a component-based install target. To support component-based installation the extra installed scripts all need to have the "clang-format" component too. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261680 91177308-0d34-0410-b5e6-96231b3b80d8 --- tools/clang-format/CMakeLists.txt | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/tools/clang-format/CMakeLists.txt b/tools/clang-format/CMakeLists.txt index 6ef0c2280f4..a13633eaefc 100644 --- a/tools/clang-format/CMakeLists.txt +++ b/tools/clang-format/CMakeLists.txt @@ -1,6 +1,6 @@ set(LLVM_LINK_COMPONENTS support) -add_clang_executable(clang-format +add_clang_tool(clang-format ClangFormat.cpp ) @@ -19,10 +19,21 @@ if( LLVM_USE_SANITIZE_COVERAGE ) add_subdirectory(fuzzer) endif() -install(TARGETS clang-format RUNTIME DESTINATION bin) -install(PROGRAMS clang-format-bbedit.applescript DESTINATION share/clang) -install(PROGRAMS clang-format-diff.py DESTINATION share/clang) -install(PROGRAMS clang-format-sublime.py DESTINATION share/clang) -install(PROGRAMS clang-format.el DESTINATION share/clang) -install(PROGRAMS clang-format.py DESTINATION share/clang) -install(PROGRAMS git-clang-format DESTINATION bin) +install(PROGRAMS clang-format-bbedit.applescript + DESTINATION share/clang + COMPONENT clang-format) +install(PROGRAMS clang-format-diff.py + DESTINATION share/clang + COMPONENT clang-format) +install(PROGRAMS clang-format-sublime.py + DESTINATION share/clang + COMPONENT clang-format) +install(PROGRAMS clang-format.el + DESTINATION share/clang + COMPONENT clang-format) +install(PROGRAMS clang-format.py + DESTINATION share/clang + COMPONENT clang-format) +install(PROGRAMS git-clang-format + DESTINATION bin + COMPONENT clang-format) From ed31f4cf0f7ce3fe7c4007867cd2825a7de35381 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Fri, 19 Feb 2016 22:43:58 +0000 Subject: [PATCH 211/742] [modules] Do less scanning of macro definition chains when computing the set of exported module macros outside local submodule visibility mode. Related to PR24667. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261373 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Lex/PPLexerChange.cpp | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/lib/Lex/PPLexerChange.cpp b/lib/Lex/PPLexerChange.cpp index 2f09841c5b5..235cc3f463b 100644 --- a/lib/Lex/PPLexerChange.cpp +++ b/lib/Lex/PPLexerChange.cpp @@ -681,21 +681,36 @@ void Preprocessor::LeaveSubmodule() { Module *LeavingMod = Info.M; SourceLocation ImportLoc = Info.ImportLoc; + if ((!getLangOpts().CompilingModule || + LeavingMod->getTopLevelModuleName() != getLangOpts().CurrentModule) && + !getLangOpts().ModulesLocalVisibility) { + // Fast path: if we're leaving a modular header that we included textually, + // and we're not building the interface for that module, and we're not + // providing submodule visibility semantics regardless, then we don't need + // to create ModuleMacros. (We'd never use them.) + BuildingSubmoduleStack.pop_back(); + makeModuleVisible(LeavingMod, ImportLoc); + return; + } + // Create ModuleMacros for any macros defined in this submodule. for (auto &Macro : CurSubmoduleState->Macros) { auto *II = const_cast(Macro.first); // Find the starting point for the MacroDirective chain in this submodule. MacroDirective *OldMD = nullptr; - if (getLangOpts().ModulesLocalVisibility) { + auto *OldState = Info.OuterSubmoduleState; + if (getLangOpts().ModulesLocalVisibility) + OldState = &NullSubmoduleState; + if (OldState && OldState != CurSubmoduleState) { // FIXME: It'd be better to start at the state from when we most recently // entered this submodule, but it doesn't really matter. - auto &PredefMacros = NullSubmoduleState.Macros; - auto PredefMacroIt = PredefMacros.find(Macro.first); - if (PredefMacroIt == PredefMacros.end()) + auto &OldMacros = OldState->Macros; + auto OldMacroIt = OldMacros.find(Macro.first); + if (OldMacroIt == OldMacros.end()) OldMD = nullptr; else - OldMD = PredefMacroIt->second.getLatest(); + OldMD = OldMacroIt->second.getLatest(); } // This module may have exported a new macro. If so, create a ModuleMacro From 4580305186db117de2582f24791bfeb8c1e5c493 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 23 Feb 2016 23:20:51 +0000 Subject: [PATCH 212/742] PR24667: fix quadratic runtime if textually-included modular headers define large numbers of macros. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261705 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Lex/Preprocessor.h | 16 ++++++-- lib/Lex/PPLexerChange.cpp | 67 +++++++++++++++++++++----------- lib/Lex/PPMacroExpansion.cpp | 7 ++++ 3 files changed, 65 insertions(+), 25 deletions(-) diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h index 5ffa48277f3..e4f8557577b 100644 --- a/include/clang/Lex/Preprocessor.h +++ b/include/clang/Lex/Preprocessor.h @@ -507,9 +507,10 @@ class Preprocessor : public RefCountedBase { /// \brief Information about a submodule that we're currently building. struct BuildingSubmoduleInfo { BuildingSubmoduleInfo(Module *M, SourceLocation ImportLoc, - SubmoduleState *OuterSubmoduleState) - : M(M), ImportLoc(ImportLoc), OuterSubmoduleState(OuterSubmoduleState) { - } + SubmoduleState *OuterSubmoduleState, + unsigned OuterPendingModuleMacroNames) + : M(M), ImportLoc(ImportLoc), OuterSubmoduleState(OuterSubmoduleState), + OuterPendingModuleMacroNames(OuterPendingModuleMacroNames) {} /// The module that we are building. Module *M; @@ -517,6 +518,8 @@ class Preprocessor : public RefCountedBase { SourceLocation ImportLoc; /// The previous SubmoduleState. SubmoduleState *OuterSubmoduleState; + /// The number of pending module macro names when we started building this. + unsigned OuterPendingModuleMacroNames; }; SmallVector BuildingSubmoduleStack; @@ -541,6 +544,9 @@ class Preprocessor : public RefCountedBase { /// The set of known macros exported from modules. llvm::FoldingSet ModuleMacros; + /// The names of potential module macros that we've not yet processed. + llvm::SmallVector PendingModuleMacroNames; + /// The list of module macros, for each identifier, that are not overridden by /// any other module macro. llvm::DenseMap> @@ -1694,6 +1700,10 @@ class Preprocessor : public RefCountedBase { void EnterSubmodule(Module *M, SourceLocation ImportLoc); void LeaveSubmodule(); + /// Determine whether we need to create module macros for #defines in the + /// current context. + bool needModuleMacros() const; + /// Update the set of active module macros and ambiguity flag for a module /// macro name. void updateModuleMacroInfo(const IdentifierInfo *II, ModuleMacroInfo &Info); diff --git a/lib/Lex/PPLexerChange.cpp b/lib/Lex/PPLexerChange.cpp index 235cc3f463b..545388a57a0 100644 --- a/lib/Lex/PPLexerChange.cpp +++ b/lib/Lex/PPLexerChange.cpp @@ -622,8 +622,8 @@ void Preprocessor::HandleMicrosoftCommentPaste(Token &Tok) { void Preprocessor::EnterSubmodule(Module *M, SourceLocation ImportLoc) { if (!getLangOpts().ModulesLocalVisibility) { // Just track that we entered this submodule. - BuildingSubmoduleStack.push_back( - BuildingSubmoduleInfo(M, ImportLoc, CurSubmoduleState)); + BuildingSubmoduleStack.push_back(BuildingSubmoduleInfo( + M, ImportLoc, CurSubmoduleState, PendingModuleMacroNames.size())); return; } @@ -664,8 +664,8 @@ void Preprocessor::EnterSubmodule(Module *M, SourceLocation ImportLoc) { } // Track that we entered this module. - BuildingSubmoduleStack.push_back( - BuildingSubmoduleInfo(M, ImportLoc, CurSubmoduleState)); + BuildingSubmoduleStack.push_back(BuildingSubmoduleInfo( + M, ImportLoc, CurSubmoduleState, PendingModuleMacroNames.size())); // Switch to this submodule as the current submodule. CurSubmoduleState = &State; @@ -675,27 +675,48 @@ void Preprocessor::EnterSubmodule(Module *M, SourceLocation ImportLoc) { makeModuleVisible(M, ImportLoc); } +bool Preprocessor::needModuleMacros() const { + // If we're not within a submodule, we never need to create ModuleMacros. + if (BuildingSubmoduleStack.empty()) + return false; + // If we are tracking module macro visibility even for textually-included + // headers, we need ModuleMacros. + if (getLangOpts().ModulesLocalVisibility) + return true; + // Otherwise, we only need module macros if we're actually compiling a module + // interface. + return !getLangOpts().CurrentModule.empty(); +} + void Preprocessor::LeaveSubmodule() { auto &Info = BuildingSubmoduleStack.back(); Module *LeavingMod = Info.M; SourceLocation ImportLoc = Info.ImportLoc; - if ((!getLangOpts().CompilingModule || - LeavingMod->getTopLevelModuleName() != getLangOpts().CurrentModule) && - !getLangOpts().ModulesLocalVisibility) { - // Fast path: if we're leaving a modular header that we included textually, - // and we're not building the interface for that module, and we're not - // providing submodule visibility semantics regardless, then we don't need - // to create ModuleMacros. (We'd never use them.) + if (!needModuleMacros() || + (!getLangOpts().ModulesLocalVisibility && + LeavingMod->getTopLevelModuleName() != getLangOpts().CurrentModule)) { + // If we don't need module macros, or this is not a module for which we + // are tracking macro visibility, don't build any, and preserve the list + // of pending names for the surrounding submodule. BuildingSubmoduleStack.pop_back(); makeModuleVisible(LeavingMod, ImportLoc); return; } // Create ModuleMacros for any macros defined in this submodule. - for (auto &Macro : CurSubmoduleState->Macros) { - auto *II = const_cast(Macro.first); + llvm::SmallPtrSet VisitedMacros; + for (unsigned I = Info.OuterPendingModuleMacroNames; + I != PendingModuleMacroNames.size(); ++I) { + auto *II = const_cast(PendingModuleMacroNames[I]); + if (!VisitedMacros.insert(II).second) + continue; + + auto MacroIt = CurSubmoduleState->Macros.find(II); + if (MacroIt == CurSubmoduleState->Macros.end()) + continue; + auto &Macro = MacroIt->second; // Find the starting point for the MacroDirective chain in this submodule. MacroDirective *OldMD = nullptr; @@ -706,7 +727,7 @@ void Preprocessor::LeaveSubmodule() { // FIXME: It'd be better to start at the state from when we most recently // entered this submodule, but it doesn't really matter. auto &OldMacros = OldState->Macros; - auto OldMacroIt = OldMacros.find(Macro.first); + auto OldMacroIt = OldMacros.find(II); if (OldMacroIt == OldMacros.end()) OldMD = nullptr; else @@ -716,16 +737,17 @@ void Preprocessor::LeaveSubmodule() { // This module may have exported a new macro. If so, create a ModuleMacro // representing that fact. bool ExplicitlyPublic = false; - for (auto *MD = Macro.second.getLatest(); MD != OldMD; - MD = MD->getPrevious()) { + for (auto *MD = Macro.getLatest(); MD != OldMD; MD = MD->getPrevious()) { assert(MD && "broken macro directive chain"); - // Stop on macros defined in other submodules we #included along the way. - // There's no point doing this if we're tracking local submodule - // visibility, since there can be no such directives in our list. + // Stop on macros defined in other submodules of this module that we + // #included along the way. There's no point doing this if we're + // tracking local submodule visibility, since there can be no such + // directives in our list. if (!getLangOpts().ModulesLocalVisibility) { Module *Mod = getModuleContainingLocation(MD->getLocation()); - if (Mod != LeavingMod) + if (Mod != LeavingMod && + Mod->getTopLevelModule() == LeavingMod->getTopLevelModule()) break; } @@ -747,13 +769,14 @@ void Preprocessor::LeaveSubmodule() { bool IsNew; // Don't bother creating a module macro if it would represent a #undef // that doesn't override anything. - if (Def || !Macro.second.getOverriddenMacros().empty()) + if (Def || !Macro.getOverriddenMacros().empty()) addModuleMacro(LeavingMod, II, Def, - Macro.second.getOverriddenMacros(), IsNew); + Macro.getOverriddenMacros(), IsNew); break; } } } + PendingModuleMacroNames.resize(Info.OuterPendingModuleMacroNames); // FIXME: Before we leave this submodule, we should parse all the other // headers within it. Otherwise, we're left with an inconsistent state diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp index 8ea4ed6c8f2..0715002e27b 100644 --- a/lib/Lex/PPMacroExpansion.cpp +++ b/lib/Lex/PPMacroExpansion.cpp @@ -52,6 +52,13 @@ void Preprocessor::appendMacroDirective(IdentifierInfo *II, MacroDirective *MD){ StoredMD.setLatest(MD); StoredMD.overrideActiveModuleMacros(*this, II); + if (needModuleMacros()) { + // Track that we created a new macro directive, so we know we should + // consider building a ModuleMacro for it when we get to the end of + // the module. + PendingModuleMacroNames.push_back(II); + } + // Set up the identifier as having associated macro history. II->setHasMacroDefinition(true); if (!MD->isDefined() && LeafModuleMacros.find(II) == LeafModuleMacros.end()) From 53694a90dd7317ce95e320cbdbd7081409e4293c Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 18 Feb 2016 16:35:59 -0800 Subject: [PATCH 213/742] Add -iapinotes-modules search path option. This option adds a new module-centric search path to find the API notes file that applies to the current module that needs to be built. It costs us only 2 stats per search path at module construction time to determine if API notes are available, making it far more efficient (and easier to use) than the prior API notes searching mechanism. It also fits much better with the future direction of the Swift Clang importer, which will soon delegate its responsibilities to Clang's API notes infrastructure. This is part of rdar://problem/24447420. --- include/clang/APINotes/APINotesManager.h | 32 ++- include/clang/APINotes/APINotesOptions.h | 37 ++++ include/clang/APINotes/APINotesReader.h | 2 +- include/clang/Driver/Options.td | 2 + include/clang/Frontend/CompilerInstance.h | 7 + include/clang/Frontend/CompilerInvocation.h | 9 + lib/APINotes/APINotesManager.cpp | 200 +++++++++++------- lib/Driver/Tools.cpp | 6 +- lib/Frontend/CompilerInstance.cpp | 7 + lib/Frontend/CompilerInvocation.cpp | 8 + lib/Sema/Sema.cpp | 3 +- lib/Sema/SemaAPINotes.cpp | 5 +- .../Inputs/APINotes/HeaderLib.apinotes | 17 ++ .../APINotes/Inputs/APINotes/SomeKit.apinotes | 34 +++ .../Modules/module.modulemap | 5 + .../Modules/module.private.modulemap | 8 + .../Modules/module_private.modulemap | 8 + test/APINotes/Inputs/Headers/module.modulemap | 3 + test/APINotes/availability.m | 3 +- test/APINotes/nullability.c | 2 +- test/APINotes/nullability.m | 2 +- test/APINotes/objc_designated_inits.m | 2 +- 22 files changed, 309 insertions(+), 93 deletions(-) create mode 100644 include/clang/APINotes/APINotesOptions.h create mode 100644 test/APINotes/Inputs/APINotes/HeaderLib.apinotes create mode 100644 test/APINotes/Inputs/APINotes/SomeKit.apinotes create mode 100644 test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.modulemap create mode 100644 test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.private.modulemap create mode 100644 test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module_private.modulemap create mode 100644 test/APINotes/Inputs/Headers/module.modulemap diff --git a/include/clang/APINotes/APINotesManager.h b/include/clang/APINotes/APINotesManager.h index 503b36536e6..69752a9c77e 100644 --- a/include/clang/APINotes/APINotesManager.h +++ b/include/clang/APINotes/APINotesManager.h @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// // -// This file defines the HeaderSearch interface. +// This file defines the APINotesManager interface. // //===----------------------------------------------------------------------===// @@ -15,15 +15,18 @@ #define LLVM_CLANG_APINOTES_APINOTESMANAGER_H #include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/StringRef.h" #include +#include namespace clang { class DirectoryEntry; class FileEntry; +class LangOptions; class SourceManager; namespace api_notes { @@ -47,6 +50,13 @@ class APINotesManager { SourceManager &SourceMgr; + /// Whether to implicitly search for API notes files based on the + /// source file from which an entity was declared. + bool ImplicitAPINotes; + + /// The API notes reader for the current module. + std::unique_ptr CurrentModuleReader; + /// Whether we have already pruned the API notes cache. bool PrunedCache; @@ -56,6 +66,13 @@ class APINotesManager { /// reader for this directory. llvm::DenseMap Readers; + /// Load the API notes associated with the given file, whether it is + /// the binary or source form of API notes. + /// + /// \returns the API notes reader for this file, or null if there is + /// a failure. + std::unique_ptr loadAPINotes(const FileEntry *apiNotesFile); + /// Load the given API notes file for the given header directory. /// /// \param HeaderDir The directory at which we @@ -78,9 +95,20 @@ class APINotesManager { bool Public); public: - APINotesManager(SourceManager &SourceMgr); + APINotesManager(SourceManager &sourceMgr, const LangOptions &langOpts); ~APINotesManager(); + /// Load the API notes for the current module. + /// + /// \param moduleName The name of the current module. + /// \param searchPaths The paths in which we should search for API notes + /// for the current module. + /// + /// \returns the file entry for the API notes file loaded, or nullptr if + /// no API notes were found. + const FileEntry *loadCurrentModuleAPINotes(StringRef moduleName, + ArrayRef searchPaths); + /// Find the API notes reader that corresponds to the given source location. APINotesReader *findAPINotes(SourceLocation Loc); }; diff --git a/include/clang/APINotes/APINotesOptions.h b/include/clang/APINotes/APINotesOptions.h new file mode 100644 index 00000000000..01c7513a1d3 --- /dev/null +++ b/include/clang/APINotes/APINotesOptions.h @@ -0,0 +1,37 @@ +//===--- APINotesOptions.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the APINotesOptions class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_APINOTES_APINOTESOPTIONS_H +#define LLVM_CLANG_APINOTES_APINOTESOPTIONS_H + +#include +#include + +namespace clang { + +/// APINotesOptions - Track various options which control how API +/// notes are found and handled. +class APINotesOptions { +public: + /// The set of search paths where we API notes can be found for + /// particular modules. + /// + /// The API notes in this directory are stored as + /// .apinotes or .apinotesc, and are only + /// applied when building the module . + std::vector ModuleSearchPaths; +}; + +} // end namespace clang + +#endif diff --git a/include/clang/APINotes/APINotesReader.h b/include/clang/APINotes/APINotesReader.h index 77b5f16bb42..4759f85c1ab 100644 --- a/include/clang/APINotes/APINotesReader.h +++ b/include/clang/APINotes/APINotesReader.h @@ -38,7 +38,7 @@ class APINotesReader { /// contains the contents of a binary API notes file. /// /// \returns the new API notes reader, or null if an error occurred. - static std::unique_ptr + static std::unique_ptr get(std::unique_ptr inputBuffer); ~APINotesReader(); diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index 55297d823c4..7abe538275d 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -1216,6 +1216,8 @@ def help : Flag<["-", "--"], "help">, Flags<[CC1Option,CC1AsOption]>, HelpText<"Display available options">; def index_header_map : Flag<["-"], "index-header-map">, Flags<[CC1Option]>, HelpText<"Make the next included directory (-I or -F) an indexer header map">; +def iapinotes_modules : JoinedOrSeparate<["-"], "iapinotes-modules">, Group, Flags<[CC1Option]>, + HelpText<"Add directory to the API notes search path referenced by module name">, MetaVarName<"">; def idirafter : JoinedOrSeparate<["-"], "idirafter">, Group, Flags<[CC1Option]>, HelpText<"Add directory to AFTER include search path">; def iframework : JoinedOrSeparate<["-"], "iframework">, Group, Flags<[CC1Option]>, diff --git a/include/clang/Frontend/CompilerInstance.h b/include/clang/Frontend/CompilerInstance.h index 83eed2cdc59..798cc2ddec1 100644 --- a/include/clang/Frontend/CompilerInstance.h +++ b/include/clang/Frontend/CompilerInstance.h @@ -291,6 +291,13 @@ class CompilerInstance : public ModuleLoader { return Invocation->getHeaderSearchOpts(); } + APINotesOptions &getAPINotesOpts() { + return Invocation->getAPINotesOpts(); + } + const APINotesOptions &getAPINotesOpts() const { + return Invocation->getAPINotesOpts(); + } + LangOptions &getLangOpts() { return *Invocation->getLangOpts(); } diff --git a/include/clang/Frontend/CompilerInvocation.h b/include/clang/Frontend/CompilerInvocation.h index 0b4a1e587e7..fa04af27627 100644 --- a/include/clang/Frontend/CompilerInvocation.h +++ b/include/clang/Frontend/CompilerInvocation.h @@ -10,6 +10,7 @@ #ifndef LLVM_CLANG_FRONTEND_COMPILERINVOCATION_H_ #define LLVM_CLANG_FRONTEND_COMPILERINVOCATION_H_ +#include "clang/APINotes/APINotesOptions.h" #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/FileSystemOptions.h" #include "clang/Basic/LangOptions.h" @@ -105,6 +106,9 @@ class CompilerInvocation : public CompilerInvocationBase { MigratorOptions MigratorOpts; + /// Options controlling API notes. + APINotesOptions APINotesOpts; + /// Options controlling IRgen and the backend. CodeGenOptions CodeGenOpts; @@ -173,6 +177,11 @@ class CompilerInvocation : public CompilerInvocationBase { const MigratorOptions &getMigratorOpts() const { return MigratorOpts; } + + APINotesOptions &getAPINotesOpts() { return APINotesOpts; } + const APINotesOptions &getAPINotesOpts() const { + return APINotesOpts; + } CodeGenOptions &getCodeGenOpts() { return CodeGenOpts; } const CodeGenOptions &getCodeGenOpts() const { diff --git a/lib/APINotes/APINotesManager.cpp b/lib/APINotes/APINotesManager.cpp index 50736939a60..3c3d5fdfc41 100644 --- a/lib/APINotes/APINotesManager.cpp +++ b/lib/APINotes/APINotesManager.cpp @@ -12,10 +12,12 @@ //===----------------------------------------------------------------------===// #include "clang/APINotes/APINotesManager.h" +#include "clang/APINotes/APINotesOptions.h" #include "clang/APINotes/APINotesReader.h" #include "clang/APINotes/APINotesYAMLCompiler.h" #include "clang/Basic/DiagnosticIDs.h" #include "clang/Basic/FileManager.h" +#include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/SourceMgrAdapter.h" #include "clang/Basic/Version.h" @@ -49,9 +51,10 @@ STATISTIC(NumBinaryCacheMisses, STATISTIC(NumBinaryCacheRebuilds, "binary form cache rebuilds"); -APINotesManager::APINotesManager(SourceManager &SourceMgr) - : SourceMgr(SourceMgr), PrunedCache(false) { } - +APINotesManager::APINotesManager(SourceManager &sourceMgr, + const LangOptions &langOpts) + : SourceMgr(sourceMgr), ImplicitAPINotes(langOpts.APINotes), + PrunedCache(false) { } APINotesManager::~APINotesManager() { // Free the API notes readers. @@ -131,155 +134,145 @@ static void pruneAPINotesCache(StringRef APINotesCachePath) { } } -bool APINotesManager::loadAPINotes(const DirectoryEntry *HeaderDir, - const FileEntry *APINotesFile) { - assert(Readers.find(HeaderDir) == Readers.end()); - - FileManager &FileMgr = SourceMgr.getFileManager(); +std::unique_ptr +APINotesManager::loadAPINotes(const FileEntry *apiNotesFile) { + FileManager &fileMgr = SourceMgr.getFileManager(); // If the API notes file is already in the binary form, load it directly. - StringRef APINotesFileName = APINotesFile->getName(); - StringRef APINotesFileExt = llvm::sys::path::extension(APINotesFileName); - if (!APINotesFileExt.empty() && - APINotesFileExt.substr(1) == BINARY_APINOTES_EXTENSION) { + StringRef apiNotesFileName = apiNotesFile->getName(); + StringRef apiNotesFileExt = llvm::sys::path::extension(apiNotesFileName); + if (!apiNotesFileExt.empty() && + apiNotesFileExt.substr(1) == BINARY_APINOTES_EXTENSION) { // Load the file. - auto Buffer = FileMgr.getBufferForFile(APINotesFile); - if (!Buffer) { - Readers[HeaderDir] = nullptr; - return true; - } + auto buffer = fileMgr.getBufferForFile(apiNotesFile); + if (!buffer) return nullptr; // Load the binary form. - auto Reader = APINotesReader::get(std::move(Buffer.get())); - if (!Reader) { - Readers[HeaderDir] = nullptr; - return true; - } - - // Record the reader. - Readers[HeaderDir] = Reader.release(); - return false; + return APINotesReader::get(std::move(buffer.get())); } // If we haven't pruned the API notes cache yet during this execution, do // so now. if (!PrunedCache) { - pruneAPINotesCache(FileMgr.getFileSystemOpts().APINotesCachePath); + pruneAPINotesCache(fileMgr.getFileSystemOpts().APINotesCachePath); PrunedCache = true; } // Compute a hash of the API notes file's directory and the Clang version, // to be used as part of the filename for the cached binary copy. - auto code = llvm::hash_value(StringRef(APINotesFile->getDir()->getName())); + auto code = llvm::hash_value(StringRef(apiNotesFile->getDir()->getName())); code = hash_combine(code, getClangFullRepositoryVersion()); // Determine the file name for the cached binary form. - SmallString<128> CompiledFileName; - CompiledFileName += FileMgr.getFileSystemOpts().APINotesCachePath; - assert(!CompiledFileName.empty() && "No API notes cache path provided?"); - llvm::sys::path::append(CompiledFileName, - (llvm::Twine(llvm::sys::path::stem(APINotesFileName)) + "-" + SmallString<128> compiledFileName; + compiledFileName += fileMgr.getFileSystemOpts().APINotesCachePath; + assert(!compiledFileName.empty() && "No API notes cache path provided?"); + llvm::sys::path::append(compiledFileName, + (llvm::Twine(llvm::sys::path::stem(apiNotesFileName)) + "-" + llvm::APInt(64, code).toString(36, /*Signed=*/false) + "." + BINARY_APINOTES_EXTENSION)); // Try to open the cached binary form. - if (const FileEntry *CompiledFile = FileMgr.getFile(CompiledFileName, + if (const FileEntry *compiledFile = fileMgr.getFile(compiledFileName, /*openFile=*/true, /*cacheFailure=*/false)) { // Load the file contents. - if (auto Buffer = FileMgr.getBufferForFile(CompiledFile)) { + if (auto buffer = fileMgr.getBufferForFile(compiledFile)) { // Make sure the file is up-to-date. - if (CompiledFile->getModificationTime() - >= APINotesFile->getModificationTime()) { + if (compiledFile->getModificationTime() + >= apiNotesFile->getModificationTime()) { // Load the file. - if (auto Reader = APINotesReader::get(std::move(Buffer.get()))) { + if (auto reader = APINotesReader::get(std::move(buffer.get()))) { // Success. ++NumBinaryCacheHits; - Readers[HeaderDir] = Reader.release(); - return false; + return reader; } } } // The cache entry was somehow broken; delete this one so we can build a // new one below. - llvm::sys::fs::remove(CompiledFileName.str()); + llvm::sys::fs::remove(compiledFileName.str()); ++NumBinaryCacheRebuilds; } else { ++NumBinaryCacheMisses; } // Open the source file. - auto Buffer = FileMgr.getBufferForFile(APINotesFile); - if (!Buffer) { - Readers[HeaderDir] = nullptr; - return true; - } + auto buffer = fileMgr.getBufferForFile(apiNotesFile); + if (!buffer) return nullptr; // Compile the API notes source into a buffer. // FIXME: Either propagate OSType through or, better yet, improve the binary // APINotes format to maintain complete availability information. - llvm::SmallVector APINotesBuffer; + llvm::SmallVector apiNotesBuffer; { SourceMgrAdapter srcMgrAdapter(SourceMgr, SourceMgr.getDiagnostics(), diag::err_apinotes_message, diag::warn_apinotes_message, diag::note_apinotes_message, - APINotesFile); - llvm::raw_svector_ostream OS(APINotesBuffer); - if (api_notes::compileAPINotes(Buffer.get()->getBuffer(), + apiNotesFile); + llvm::raw_svector_ostream OS(apiNotesBuffer); + if (api_notes::compileAPINotes(buffer.get()->getBuffer(), OS, api_notes::OSType::Absent, srcMgrAdapter.getDiagHandler(), - srcMgrAdapter.getDiagContext())) { - Readers[HeaderDir] = nullptr; - return true; - } + srcMgrAdapter.getDiagContext())) + return nullptr; // Make a copy of the compiled form into the buffer. - Buffer = llvm::MemoryBuffer::getMemBufferCopy( - StringRef(APINotesBuffer.data(), APINotesBuffer.size())); + buffer = llvm::MemoryBuffer::getMemBufferCopy( + StringRef(apiNotesBuffer.data(), apiNotesBuffer.size())); } // Save the binary form into the cache. Perform this operation // atomically. - SmallString<64> TemporaryBinaryFileName = CompiledFileName.str(); - TemporaryBinaryFileName.erase( - TemporaryBinaryFileName.end() - - llvm::sys::path::extension(TemporaryBinaryFileName).size(), - TemporaryBinaryFileName.end()); - TemporaryBinaryFileName += "-%%%%%%."; - TemporaryBinaryFileName += BINARY_APINOTES_EXTENSION; - - int TemporaryFD; + SmallString<64> temporaryBinaryFileName = compiledFileName.str(); + temporaryBinaryFileName.erase( + temporaryBinaryFileName.end() + - llvm::sys::path::extension(temporaryBinaryFileName).size(), + temporaryBinaryFileName.end()); + temporaryBinaryFileName += "-%%%%%%."; + temporaryBinaryFileName += BINARY_APINOTES_EXTENSION; + + int temporaryFD; llvm::sys::fs::create_directories( - FileMgr.getFileSystemOpts().APINotesCachePath); - if (!llvm::sys::fs::createUniqueFile(TemporaryBinaryFileName.str(), - TemporaryFD, TemporaryBinaryFileName)) { + fileMgr.getFileSystemOpts().APINotesCachePath); + if (!llvm::sys::fs::createUniqueFile(temporaryBinaryFileName.str(), + temporaryFD, temporaryBinaryFileName)) { // Write the contents of the buffer. bool hadError; { - llvm::raw_fd_ostream Out(TemporaryFD, /*shouldClose=*/true); - Out.write(Buffer.get()->getBufferStart(), Buffer.get()->getBufferSize()); - Out.flush(); + llvm::raw_fd_ostream out(temporaryFD, /*shouldClose=*/true); + out.write(buffer.get()->getBufferStart(), buffer.get()->getBufferSize()); + out.flush(); - hadError = Out.has_error(); + hadError = out.has_error(); } if (!hadError) { // Rename the temporary file to the actual compiled file. - llvm::sys::fs::rename(TemporaryBinaryFileName.str(), - CompiledFileName.str()); + llvm::sys::fs::rename(temporaryBinaryFileName.str(), + compiledFileName.str()); } } // Load the binary form we just compiled. - auto Reader = APINotesReader::get(std::move(*Buffer)); - assert(Reader && "Could not load the API notes we just generated?"); + auto reader = APINotesReader::get(std::move(*buffer)); + assert(reader && "Could not load the API notes we just generated?"); + return reader; +} - // Record the reader. - Readers[HeaderDir] = Reader.release(); - return false; +bool APINotesManager::loadAPINotes(const DirectoryEntry *HeaderDir, + const FileEntry *APINotesFile) { + assert(Readers.find(HeaderDir) == Readers.end()); + if (auto reader = loadAPINotes(APINotesFile)) { + Readers[HeaderDir] = reader.release(); + return false; + } + + Readers[HeaderDir] = nullptr; + return true; } const DirectoryEntry *APINotesManager::loadFrameworkAPINotes( @@ -332,7 +325,56 @@ const DirectoryEntry *APINotesManager::loadFrameworkAPINotes( return HeaderDir; } +const FileEntry *APINotesManager::loadCurrentModuleAPINotes( + StringRef moduleName, + ArrayRef searchPaths) { + assert(!CurrentModuleReader && + "Already loaded API notes for the current module?"); + + FileManager &fileMgr = SourceMgr.getFileManager(); + + // Look for API notes for this module in the module search paths. + for (const auto &searchPath : searchPaths) { + // First, look for a binary API notes file. + llvm::SmallString<128> apiNotesFilePath; + apiNotesFilePath += searchPath; + llvm::sys::path::append( + apiNotesFilePath, + llvm::Twine(moduleName) + "." + BINARY_APINOTES_EXTENSION); + + // Try to open the binary API Notes file. + if (const FileEntry *binaryAPINotesFile + = fileMgr.getFile(apiNotesFilePath)) { + CurrentModuleReader = loadAPINotes(binaryAPINotesFile); + return CurrentModuleReader ? binaryAPINotesFile : nullptr; + } + + // Try to open the source API Notes file. + apiNotesFilePath = searchPath; + llvm::sys::path::append( + apiNotesFilePath, + llvm::Twine(moduleName) + "." + SOURCE_APINOTES_EXTENSION); + if (const FileEntry *sourceAPINotesFile + = fileMgr.getFile(apiNotesFilePath)) { + CurrentModuleReader = loadAPINotes(sourceAPINotesFile); + return CurrentModuleReader ? sourceAPINotesFile : nullptr; + } + } + + // Didn't find any API notes. + return nullptr; +} + APINotesReader *APINotesManager::findAPINotes(SourceLocation Loc) { + // If there is a reader for the current module, return it. + if (CurrentModuleReader) return CurrentModuleReader.get(); + + // If we're not allowed to implicitly load API notes files, we're done. + if (!ImplicitAPINotes) return nullptr; + + // If we don't have source location information, we're done. + if (Loc.isInvalid()) return nullptr; + // API notes are associated with the expansion location. Retrieve the // file for this location. SourceLocation ExpansionLoc = SourceMgr.getExpansionLoc(Loc); diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index 0d62db737c6..6e496b8d3ef 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -4842,8 +4842,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back("-fno-assume-sane-operator-new"); if (Args.hasFlag(options::OPT_fapinotes, options::OPT_fno_apinotes, - false)) { - CmdArgs.push_back("-fapinotes"); + false) || + Args.hasArg(options::OPT_iapinotes_modules)) { + if (Args.hasFlag(options::OPT_fapinotes, options::OPT_fno_apinotes, false)) + CmdArgs.push_back("-fapinotes"); SmallString<128> APINotesCachePath; if (Arg *A = Args.getLastArg(options::OPT_fapinotes_cache_path)) { diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp index d33b631c0df..acb983af6a3 100644 --- a/lib/Frontend/CompilerInstance.cpp +++ b/lib/Frontend/CompilerInstance.cpp @@ -531,6 +531,13 @@ void CompilerInstance::createSema(TranslationUnitKind TUKind, CodeCompleteConsumer *CompletionConsumer) { TheSema.reset(new Sema(getPreprocessor(), getASTContext(), getASTConsumer(), TUKind, CompletionConsumer)); + + // If we're building a module, notify the API notes manager. + if (!getLangOpts().CurrentModule.empty()) { + (void)TheSema->APINotes.loadCurrentModuleAPINotes( + getLangOpts().CurrentModule, + getAPINotesOpts().ModuleSearchPaths); + } } // Output Files diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index b27fd09b7da..e9037e8444b 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1312,6 +1312,12 @@ static void ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args) { Opts.AddVFSOverlayFile(A->getValue()); } +static void ParseAPINotesArgs(APINotesOptions &Opts, ArgList &Args) { + using namespace options; + for (const Arg *A : Args.filtered(OPT_iapinotes_modules)) + Opts.ModuleSearchPaths.push_back(A->getValue()); +} + void CompilerInvocation::setLangDefaults(LangOptions &Opts, InputKind IK, LangStandard::Kind LangStd) { // Set some properties which depend solely on the input kind; it would be nice @@ -2077,6 +2083,8 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, Success &= ParseCodeGenArgs(Res.getCodeGenOpts(), Args, DashX, Diags, Res.getTargetOpts()); ParseHeaderSearchArgs(Res.getHeaderSearchOpts(), Args); + ParseAPINotesArgs(Res.getAPINotesOpts(), Args); + if (DashX == IK_AST || DashX == IK_LLVM_IR) { // ObjCAAutoRefCount and Sanitize LangOpts are used to setup the // PassManager in BackendUtil.cpp. They need to be initializd no matter diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index 34261f2556c..5b93d73c847 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -77,7 +77,8 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, isMultiplexExternalSource(false), FPFeatures(pp.getLangOpts()), LangOpts(pp.getLangOpts()), PP(pp), Context(ctxt), Consumer(consumer), Diags(PP.getDiagnostics()), SourceMgr(PP.getSourceManager()), - APINotes(SourceMgr), CollectStats(false), CodeCompleter(CodeCompleter), + APINotes(SourceMgr, LangOpts), CollectStats(false), + CodeCompleter(CodeCompleter), CurContext(nullptr), OriginalLexicalContext(nullptr), PackContext(nullptr), MSStructPragmaOn(false), MSPointerToMemberRepresentationMethod( diff --git a/lib/Sema/SemaAPINotes.cpp b/lib/Sema/SemaAPINotes.cpp index 3114805da20..7817754c9f2 100644 --- a/lib/Sema/SemaAPINotes.cpp +++ b/lib/Sema/SemaAPINotes.cpp @@ -208,10 +208,7 @@ static void ProcessAPINotes(Sema &S, ObjCContainerDecl *D, /// Process API notes that are associated with this declaration, mapping them /// to attributes as appropriate. void Sema::ProcessAPINotes(Decl *D) { - if (!Context.getLangOpts().APINotes) - return; - - if (!D || D->getLocation().isInvalid()) + if (!D) return; // Globals. diff --git a/test/APINotes/Inputs/APINotes/HeaderLib.apinotes b/test/APINotes/Inputs/APINotes/HeaderLib.apinotes new file mode 100644 index 00000000000..a4ddafe2892 --- /dev/null +++ b/test/APINotes/Inputs/APINotes/HeaderLib.apinotes @@ -0,0 +1,17 @@ +Name: HeaderLib +Functions: + - Name: custom_realloc + NullabilityOfRet: N + Nullability: [ N, S ] + - Name: unavailable_function + Availability: none + AvailabilityMsg: "I beg you not to use this" + - Name: do_something_with_pointers + NullabilityOfRet: O + Nullability: [ N, O ] + +Globals: + - Name: global_int + Nullability: N + - Name: unavailable_global_int + Availability: none diff --git a/test/APINotes/Inputs/APINotes/SomeKit.apinotes b/test/APINotes/Inputs/APINotes/SomeKit.apinotes new file mode 100644 index 00000000000..e7004120126 --- /dev/null +++ b/test/APINotes/Inputs/APINotes/SomeKit.apinotes @@ -0,0 +1,34 @@ +Name: SomeKit +Classes: + - Name: A + Methods: + - Selector: "transform:" + MethodKind: Instance + Availability: none + AvailabilityMsg: "anything but this" + - Selector: "transform:integer:" + MethodKind: Instance + NullabilityOfRet: N + Nullability: [ N, S ] + - Selector: "privateTransform:input:" + MethodKind: Instance + NullabilityOfRet: N + Nullability: [ N, S ] + Properties: + - Name: intValue + Availability: none + AvailabilityMsg: "wouldn't work anyway" + - Name: internalProperty + Nullability: N + - Name: B + Availability: none + AvailabilityMsg: "just don't" + - Name: C + Methods: + - Selector: "initWithA:" + MethodKind: Instance + DesignatedInit: true +Protocols: + - Name: InternalProtocol + Availability: none + AvailabilityMsg: "not for you" diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.modulemap b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.modulemap new file mode 100644 index 00000000000..3abee2df0be --- /dev/null +++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.modulemap @@ -0,0 +1,5 @@ +framework module SomeKit { + umbrella header "SomeKit.h" + export * + module * { export * } +} diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.private.modulemap b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.private.modulemap new file mode 100644 index 00000000000..bbda9d08e39 --- /dev/null +++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.private.modulemap @@ -0,0 +1,8 @@ +module SomeKit.Private { + header "SomeKit_Private.h" + export * + + explicit module NullAnnotation { + header "SomeKit_PrivateForNullAnnotation.h" + } +} diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module_private.modulemap b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module_private.modulemap new file mode 100644 index 00000000000..e31034317cb --- /dev/null +++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module_private.modulemap @@ -0,0 +1,8 @@ +explicit framework module SomeKit.Private { + header "SomeKit_Private.h" + explicit NullAnnotation { header "SomeKit_PrivateForNullAnnotation.h" } + export * + module * { export * } +syntax error + +} diff --git a/test/APINotes/Inputs/Headers/module.modulemap b/test/APINotes/Inputs/Headers/module.modulemap new file mode 100644 index 00000000000..3e59efcf2c4 --- /dev/null +++ b/test/APINotes/Inputs/Headers/module.modulemap @@ -0,0 +1,3 @@ +module HeaderLib { + header "HeaderLib.h" +} diff --git a/test/APINotes/availability.m b/test/APINotes/availability.m index 177700d6311..5b996ec8c94 100644 --- a/test/APINotes/availability.m +++ b/test/APINotes/availability.m @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// RUN: rm -rf %t +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify #include "HeaderLib.h" #import diff --git a/test/APINotes/nullability.c b/test/APINotes/nullability.c index 940587e8e0a..54b0df6cea9 100644 --- a/test/APINotes/nullability.c +++ b/test/APINotes/nullability.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify #include "HeaderLib.h" diff --git a/test/APINotes/nullability.m b/test/APINotes/nullability.m index 40901f2c4fd..ba51cd64d82 100644 --- a/test/APINotes/nullability.m +++ b/test/APINotes/nullability.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify #import diff --git a/test/APINotes/objc_designated_inits.m b/test/APINotes/objc_designated_inits.m index 194d135b6c0..5eb5fa103d5 100644 --- a/test/APINotes/objc_designated_inits.m +++ b/test/APINotes/objc_designated_inits.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify #include "HeaderLib.h" #import From 004180674813644d5be4ef71d0f311f28db6b1c3 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 18 Feb 2016 16:36:27 -0800 Subject: [PATCH 214/742] Map FactoryAsInit: C to a hidden attribute. The FactoryAsInit entry in API notes wasn't getting mapped to any Clang attributes. Since there is no use for such an attribute purely in Objective-C, map it to a new unspellable attribute (SwiftSuppressFactoryAsInitAttr) used only to appropriately annotate the declaration for consumption by the Swift Clang importer. Part of rdar://problem/24447420. --- include/clang/Basic/Attr.td | 8 ++++++++ lib/Sema/SemaAPINotes.cpp | 6 ++++++ test/APINotes/Inputs/APINotes/SomeKit.apinotes | 6 ++++++ .../Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h | 4 ++++ 4 files changed, 24 insertions(+) diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 1b6641c2317..8ff03ce30d9 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -1334,6 +1334,14 @@ def SwiftPrivate : InheritableAttr { let Documentation = [Undocumented]; } +def SwiftSuppressFactoryAsInit : InheritableAttr { + // This attribute has no spellings as it is only ever created implicitly + // from API notes. + let Spellings = []; + let SemaHandler = 0; + let Documentation = [Undocumented]; +} + def ReqdWorkGroupSize : InheritableAttr { let Spellings = [GNU<"reqd_work_group_size">]; let Args = [UnsignedArgument<"XDim">, UnsignedArgument<"YDim">, diff --git a/lib/Sema/SemaAPINotes.cpp b/lib/Sema/SemaAPINotes.cpp index 7817754c9f2..8ca364eec19 100644 --- a/lib/Sema/SemaAPINotes.cpp +++ b/lib/Sema/SemaAPINotes.cpp @@ -192,6 +192,12 @@ static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D, } } + if (Info.getFactoryAsInitKind() + == api_notes::FactoryAsInitKind::AsClassMethod && + !D->getAttr()) { + D->addAttr(SwiftSuppressFactoryAsInitAttr::CreateImplicit(S.Context)); + } + // Handle common function information. ProcessAPINotes(S, FunctionOrMethod(D), static_cast(Info)); diff --git a/test/APINotes/Inputs/APINotes/SomeKit.apinotes b/test/APINotes/Inputs/APINotes/SomeKit.apinotes index e7004120126..d251491ed43 100644 --- a/test/APINotes/Inputs/APINotes/SomeKit.apinotes +++ b/test/APINotes/Inputs/APINotes/SomeKit.apinotes @@ -28,6 +28,12 @@ Classes: - Selector: "initWithA:" MethodKind: Instance DesignatedInit: true + - Name: ProcessInfo + Methods: + - Selector: "processInfo" + MethodKind: Class + FactoryAsInit: C + Protocols: - Name: InternalProtocol Availability: none diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h index 01b003d1eee..5e0d7e0b7ab 100644 --- a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h +++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h @@ -20,4 +20,8 @@ __attribute__((objc_root_class)) - (instancetype)initWithA:(A*)a; @end +@interface ProcessInfo : A ++(instancetype)processInfo; +@end + #endif From 5f54238ee6aa247932997f24dea3529c9e06465d Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 18 Feb 2016 16:39:30 -0800 Subject: [PATCH 215/742] Add "nonswift" availability mapping to Swift unavailability. The new "nonswift" availability maps to __attribute__((swift,unavailable)). Part of rdar://problem/24447420. --- include/clang/APINotes/Types.h | 16 ++++++++++++++-- lib/APINotes/APINotesFormat.h | 2 +- lib/APINotes/APINotesReader.cpp | 4 +++- lib/APINotes/APINotesWriter.cpp | 2 +- lib/APINotes/APINotesYAMLCompiler.cpp | 14 +++++++++++--- lib/Sema/SemaAPINotes.cpp | 10 ++++++++++ 6 files changed, 40 insertions(+), 8 deletions(-) diff --git a/include/clang/APINotes/Types.h b/include/clang/APINotes/Types.h index 8173bf4f3a2..3cf149357bb 100644 --- a/include/clang/APINotes/Types.h +++ b/include/clang/APINotes/Types.h @@ -67,12 +67,16 @@ class CommonEntityInfo { /// Whether this entity is marked unavailable. unsigned Unavailable : 1; - CommonEntityInfo() : Unavailable(0) { } + /// Whether this entity is marked unavailable in Swift. + unsigned UnavailableInSwift : 1; + + CommonEntityInfo() : Unavailable(0), UnavailableInSwift(0) { } friend bool operator==(const CommonEntityInfo &lhs, const CommonEntityInfo &rhs) { return lhs.UnavailableMsg == rhs.UnavailableMsg && - lhs.Unavailable == rhs.Unavailable; + lhs.Unavailable == rhs.Unavailable && + lhs.UnavailableInSwift == rhs.UnavailableInSwift; } friend bool operator!=(const CommonEntityInfo &lhs, @@ -91,6 +95,14 @@ class CommonEntityInfo { } } + if (rhs.UnavailableInSwift) { + lhs.UnavailableInSwift = true; + if (rhs.UnavailableMsg.length() != 0 && + lhs.UnavailableMsg.length() == 0) { + lhs.UnavailableMsg = rhs.UnavailableMsg; + } + } + return lhs; } diff --git a/lib/APINotes/APINotesFormat.h b/lib/APINotes/APINotesFormat.h index 5462e5a5586..0801d2ceefd 100644 --- a/lib/APINotes/APINotesFormat.h +++ b/lib/APINotes/APINotesFormat.h @@ -35,7 +35,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 6; +const uint16_t VERSION_MINOR = 7; using IdentifierID = Fixnum<31>; using IdentifierIDField = BCVBR<16>; diff --git a/lib/APINotes/APINotesReader.cpp b/lib/APINotes/APINotesReader.cpp index a84f2d70491..c768f3a3df7 100644 --- a/lib/APINotes/APINotesReader.cpp +++ b/lib/APINotes/APINotesReader.cpp @@ -30,7 +30,9 @@ using namespace llvm; namespace { /// Read serialized CommonEntityInfo. void readCommonEntityInfo(const uint8_t *&data, CommonEntityInfo &info) { - info.Unavailable = *data++; + uint8_t unavailableBits = *data++; + info.Unavailable = (unavailableBits >> 1) & 0x01; + info.UnavailableInSwift = unavailableBits & 0x01; unsigned msgLength = endian::readNext(data); info.UnavailableMsg diff --git a/lib/APINotes/APINotesWriter.cpp b/lib/APINotes/APINotesWriter.cpp index 400bd08e013..0e498a797a3 100644 --- a/lib/APINotes/APINotesWriter.cpp +++ b/lib/APINotes/APINotesWriter.cpp @@ -272,7 +272,7 @@ namespace { static void emitCommonEntityInfo(raw_ostream &out, const CommonEntityInfo &info) { endian::Writer writer(out); - writer.write(info.Unavailable); + writer.write(info.Unavailable << 1 | info.UnavailableInSwift); writer.write(info.UnavailableMsg.size()); out.write(info.UnavailableMsg.c_str(), info.UnavailableMsg.size()); } diff --git a/lib/APINotes/APINotesYAMLCompiler.cpp b/lib/APINotes/APINotesYAMLCompiler.cpp index 1f299873ce3..8c27a01e5ed 100644 --- a/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/lib/APINotes/APINotesYAMLCompiler.cpp @@ -44,7 +44,7 @@ Availability: OSX # Optional: Specifies which platform the API is # available on. [OSX / iOS / none/ - # available] + # available / nonswift] AvailabilityMsg: "" # Optional: Custom availability message to display to # the user, when API is not available. @@ -143,6 +143,7 @@ namespace { OSX, IOS, None, + NonSwift, }; enum class MethodKind { @@ -264,6 +265,7 @@ namespace llvm { io.enumCase(value, "OSX", APIAvailability::OSX); io.enumCase(value, "iOS", APIAvailability::IOS); io.enumCase(value, "none", APIAvailability::None); + io.enumCase(value, "nonswift", APIAvailability::NonSwift); io.enumCase(value, "available", APIAvailability::Available); } }; @@ -410,9 +412,10 @@ static bool compile(const Module &module, bool convertAvailability(const AvailabilityItem &in, CommonEntityInfo &outInfo, llvm::StringRef apiName) { - // Populate the 'Unavailable' information. + // Populate the unavailability information. outInfo.Unavailable = (in.Mode == APIAvailability::None); - if (outInfo.Unavailable) { + outInfo.UnavailableInSwift = (in.Mode == APIAvailability::NonSwift); + if (outInfo.Unavailable || outInfo.UnavailableInSwift) { outInfo.UnavailableMsg = in.Msg; } else { if (!in.Msg.empty()) { @@ -716,6 +719,11 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, availability.Mode = APIAvailability::None; availability.Msg = copyString(info.UnavailableMsg); } + + if (info.UnavailableInSwift) { + availability.Mode = APIAvailability::NonSwift; + availability.Msg = copyString(info.UnavailableMsg); + } } /// Map nullability information for a function. diff --git a/lib/Sema/SemaAPINotes.cpp b/lib/Sema/SemaAPINotes.cpp index 8ca364eec19..6f08fd4c923 100644 --- a/lib/Sema/SemaAPINotes.cpp +++ b/lib/Sema/SemaAPINotes.cpp @@ -99,6 +99,16 @@ static void ProcessAPINotes(Sema &S, Decl *D, if (Info.Unavailable && !D->hasAttr()) { D->addAttr(UnavailableAttr::CreateImplicit(S.Context, Info.UnavailableMsg)); } + + if (Info.UnavailableInSwift) { + D->addAttr(AvailabilityAttr::CreateImplicit(S.Context, + &S.Context.Idents.get("swift"), + VersionTuple(), + VersionTuple(), + VersionTuple(), + /*Unavailable=*/true, + Info.UnavailableMsg)); + } } /// Process API notes for a variable or property. From ce0706172b1e4ba2ec21e11c3a0eb7a2701eaf6f Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Sat, 20 Feb 2016 21:35:48 -0800 Subject: [PATCH 216/742] [API Notes] Add support for SwiftName on anything, which maps to the swift_name attribute. This allows API notes to be used to override the Swift names of Objective-C entities. --- include/clang/APINotes/Types.h | 12 +++++++-- lib/APINotes/APINotesFormat.h | 2 +- lib/APINotes/APINotesReader.cpp | 7 +++++ lib/APINotes/APINotesWriter.cpp | 4 ++- lib/APINotes/APINotesYAMLCompiler.cpp | 21 +++++++++++++++ lib/Sema/SemaAPINotes.cpp | 34 +++++++++++++++++++------ test/APINotes/Inputs/roundtrip.apinotes | 13 ++++++++++ 7 files changed, 81 insertions(+), 12 deletions(-) diff --git a/include/clang/APINotes/Types.h b/include/clang/APINotes/Types.h index 3cf149357bb..79f34ccc218 100644 --- a/include/clang/APINotes/Types.h +++ b/include/clang/APINotes/Types.h @@ -58,7 +58,7 @@ class ContextID { /// Describes API notes data for any entity. /// -/// This is used as the base of +/// This is used as the base of all API notes. class CommonEntityInfo { public: /// Message to use when this entity is unavailable. @@ -70,13 +70,17 @@ class CommonEntityInfo { /// Whether this entity is marked unavailable in Swift. unsigned UnavailableInSwift : 1; + /// Swift name of this entity. + std::string SwiftName; + CommonEntityInfo() : Unavailable(0), UnavailableInSwift(0) { } friend bool operator==(const CommonEntityInfo &lhs, const CommonEntityInfo &rhs) { return lhs.UnavailableMsg == rhs.UnavailableMsg && lhs.Unavailable == rhs.Unavailable && - lhs.UnavailableInSwift == rhs.UnavailableInSwift; + lhs.UnavailableInSwift == rhs.UnavailableInSwift && + lhs.SwiftName == rhs.SwiftName; } friend bool operator!=(const CommonEntityInfo &lhs, @@ -103,6 +107,10 @@ class CommonEntityInfo { } } + if (rhs.SwiftName.length() != 0 && + lhs.SwiftName.length() == 0) + lhs.SwiftName = rhs.SwiftName; + return lhs; } diff --git a/lib/APINotes/APINotesFormat.h b/lib/APINotes/APINotesFormat.h index 0801d2ceefd..79241042951 100644 --- a/lib/APINotes/APINotesFormat.h +++ b/lib/APINotes/APINotesFormat.h @@ -35,7 +35,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 7; +const uint16_t VERSION_MINOR = 8; using IdentifierID = Fixnum<31>; using IdentifierIDField = BCVBR<16>; diff --git a/lib/APINotes/APINotesReader.cpp b/lib/APINotes/APINotesReader.cpp index c768f3a3df7..25831287c02 100644 --- a/lib/APINotes/APINotesReader.cpp +++ b/lib/APINotes/APINotesReader.cpp @@ -39,6 +39,13 @@ namespace { = std::string(reinterpret_cast(data), reinterpret_cast(data) + msgLength); data += msgLength; + + unsigned swiftNameLength + = endian::readNext(data); + info.SwiftName + = std::string(reinterpret_cast(data), + reinterpret_cast(data) + swiftNameLength); + data += swiftNameLength; } /// Used to deserialize the on-disk identifier table. diff --git a/lib/APINotes/APINotesWriter.cpp b/lib/APINotes/APINotesWriter.cpp index 0e498a797a3..a9d83d95fd9 100644 --- a/lib/APINotes/APINotesWriter.cpp +++ b/lib/APINotes/APINotesWriter.cpp @@ -265,7 +265,7 @@ namespace { /// Retrieve the serialized size of the given CommonEntityInfo, for use in /// on-disk hash tables. static unsigned getCommonEntityInfoSize(const CommonEntityInfo &info) { - return 3 + info.UnavailableMsg.size(); + return 5 + info.UnavailableMsg.size() + info.SwiftName.size(); } /// Emit a serialized representation of the common entity information. @@ -275,6 +275,8 @@ namespace { writer.write(info.Unavailable << 1 | info.UnavailableInSwift); writer.write(info.UnavailableMsg.size()); out.write(info.UnavailableMsg.c_str(), info.UnavailableMsg.size()); + writer.write(info.SwiftName.size()); + out.write(info.SwiftName.c_str(), info.SwiftName.size()); } /// Used to serialize the on-disk Objective-C context table. diff --git a/lib/APINotes/APINotesYAMLCompiler.cpp b/lib/APINotes/APINotesYAMLCompiler.cpp index 8c27a01e5ed..8896c40cda0 100644 --- a/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/lib/APINotes/APINotesYAMLCompiler.cpp @@ -168,6 +168,7 @@ namespace { NullabilitySeq Nullability; llvm::Optional NullabilityOfRet; AvailabilityItem Availability; + StringRef SwiftName; api_notes::FactoryAsInitKind FactoryAsInit = api_notes::FactoryAsInitKind::Infer; bool DesignatedInit = false; @@ -179,6 +180,7 @@ namespace { StringRef Name; llvm::Optional Nullability; AvailabilityItem Availability; + StringRef SwiftName; }; typedef std::vector PropertiesSeq; @@ -186,6 +188,7 @@ namespace { StringRef Name; bool AuditedForNullability = false; AvailabilityItem Availability; + StringRef SwiftName; MethodsSeq Methods; PropertiesSeq Properties; }; @@ -196,6 +199,7 @@ namespace { NullabilitySeq Nullability; llvm::Optional NullabilityOfRet; AvailabilityItem Availability; + StringRef SwiftName; }; typedef std::vector FunctionsSeq; @@ -203,6 +207,7 @@ namespace { StringRef Name; llvm::Optional Nullability; AvailabilityItem Availability; + StringRef SwiftName; }; typedef std::vector GlobalVariablesSeq; @@ -278,6 +283,7 @@ namespace llvm { AbsentNullability); io.mapOptional("Availability", p.Availability.Mode); io.mapOptional("AvailabilityMsg", p.Availability.Msg); + io.mapOptional("SwiftName", p.SwiftName); } }; @@ -291,6 +297,7 @@ namespace llvm { AbsentNullability); io.mapOptional("Availability", m.Availability.Mode); io.mapOptional("AvailabilityMsg", m.Availability.Msg); + io.mapOptional("SwiftName", m.SwiftName); io.mapOptional("FactoryAsInit", m.FactoryAsInit, api_notes::FactoryAsInitKind::Infer); io.mapOptional("DesignatedInit", m.DesignatedInit, false); @@ -305,6 +312,7 @@ namespace llvm { io.mapOptional("AuditedForNullability", c.AuditedForNullability, false); io.mapOptional("Availability", c.Availability.Mode); io.mapOptional("AvailabilityMsg", c.Availability.Msg); + io.mapOptional("SwiftName", c.SwiftName); io.mapOptional("Methods", c.Methods); io.mapOptional("Properties", c.Properties); } @@ -319,6 +327,7 @@ namespace llvm { AbsentNullability); io.mapOptional("Availability", f.Availability.Mode); io.mapOptional("AvailabilityMsg", f.Availability.Msg); + io.mapOptional("SwiftName", f.SwiftName); } }; @@ -330,6 +339,7 @@ namespace llvm { AbsentNullability); io.mapOptional("Availability", v.Availability.Mode); io.mapOptional("AvailabilityMsg", v.Availability.Msg); + io.mapOptional("SwiftName", v.SwiftName); } }; @@ -463,6 +473,7 @@ static bool compile(const Module &module, return; convertAvailability(meth.Availability, mInfo, meth.Selector); + mInfo.SwiftName = meth.SwiftName; // Check if the selector ends with ':' to determine if it takes arguments. bool takesArguments = meth.Selector.endswith(":"); @@ -505,6 +516,7 @@ static bool compile(const Module &module, return; convertAvailability(cl.Availability, cInfo, cl.Name); + cInfo.SwiftName = cl.SwiftName; if (cl.AuditedForNullability) cInfo.setDefaultNullability(*DefaultNullability); @@ -545,6 +557,7 @@ static bool compile(const Module &module, if (!isAvailable(prop.Availability)) continue; convertAvailability(prop.Availability, pInfo, prop.Name); + pInfo.SwiftName = prop.SwiftName; if (prop.Nullability) pInfo.setNullabilityAudited(*prop.Nullability); Writer->addObjCProperty(clID, prop.Name, pInfo); @@ -598,6 +611,7 @@ static bool compile(const Module &module, if (!isAvailable(global.Availability)) continue; convertAvailability(global.Availability, info, global.Name); + info.SwiftName = global.SwiftName; if (global.Nullability) info.setNullabilityAudited(*global.Nullability); Writer->addGlobalVariable(global.Name, info); @@ -617,6 +631,7 @@ static bool compile(const Module &module, if (!isAvailable(function.Availability)) continue; convertAvailability(function.Availability, info, function.Name); + info.SwiftName = function.SwiftName; convertNullability(function.Nullability, function.NullabilityOfRet, info, function.Name); @@ -707,6 +722,8 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, // Handle class information. handleAvailability(record.Availability, info); + record.SwiftName = copyString(info.SwiftName); + if (info.getDefaultNullability()) { record.AuditedForNullability = true; } @@ -771,6 +788,7 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, handleNullability(method.Nullability, method.NullabilityOfRet, info, selector.count(':')); handleAvailability(method.Availability, info); + method.SwiftName = copyString(info.SwiftName); method.FactoryAsInit = info.getFactoryAsInitKind(); method.DesignatedInit = info.DesignatedInit; method.Required = info.Required; @@ -787,6 +805,7 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, Property property; property.Name = name; handleAvailability(property.Availability, info); + property.SwiftName = copyString(info.SwiftName); // FIXME: No way to represent "not audited for nullability". if (auto nullability = info.getNullability()) { @@ -805,6 +824,7 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, Function function; function.Name = name; handleAvailability(function.Availability, info); + function.SwiftName = copyString(info.SwiftName); if (info.NumAdjustedNullable > 0) handleNullability(function.Nullability, function.NullabilityOfRet, info, info.NumAdjustedNullable-1); @@ -817,6 +837,7 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, GlobalVariable global; global.Name = name; handleAvailability(global.Availability, info); + global.SwiftName = copyString(info.SwiftName); // FIXME: No way to represent "not audited for nullability". if (auto nullability = info.getNullability()) { diff --git a/lib/Sema/SemaAPINotes.cpp b/lib/Sema/SemaAPINotes.cpp index 6f08fd4c923..649e8c45597 100644 --- a/lib/Sema/SemaAPINotes.cpp +++ b/lib/Sema/SemaAPINotes.cpp @@ -93,21 +93,39 @@ static void applyNullability(Sema &S, Decl *decl, NullabilityKind nullability) { } } +/// 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(mem), string.size()); +} + static void ProcessAPINotes(Sema &S, Decl *D, const api_notes::CommonEntityInfo &Info) { // Availability if (Info.Unavailable && !D->hasAttr()) { - D->addAttr(UnavailableAttr::CreateImplicit(S.Context, Info.UnavailableMsg)); + D->addAttr(UnavailableAttr::CreateImplicit(S.Context, + CopyString(S.Context, + Info.UnavailableMsg))); } if (Info.UnavailableInSwift) { - D->addAttr(AvailabilityAttr::CreateImplicit(S.Context, - &S.Context.Idents.get("swift"), - VersionTuple(), - VersionTuple(), - VersionTuple(), - /*Unavailable=*/true, - Info.UnavailableMsg)); + D->addAttr(AvailabilityAttr::CreateImplicit( + S.Context, + &S.Context.Idents.get("swift"), + VersionTuple(), + VersionTuple(), + VersionTuple(), + /*Unavailable=*/true, + CopyString(S.Context, Info.UnavailableMsg), + /*Nopartial=*/true)); + } + + // swift_name + if (!Info.SwiftName.empty() && !D->hasAttr()) { + D->addAttr(SwiftNameAttr::CreateImplicit(S.Context, + CopyString(S.Context, + Info.SwiftName))); } } diff --git a/test/APINotes/Inputs/roundtrip.apinotes b/test/APINotes/Inputs/roundtrip.apinotes index b1722406663..a1b60e5b19f 100644 --- a/test/APINotes/Inputs/roundtrip.apinotes +++ b/test/APINotes/Inputs/roundtrip.apinotes @@ -6,12 +6,14 @@ Classes: - Name: NSCell Availability: available AvailabilityMsg: '' + SwiftName: '' Methods: - Selector: init MethodKind: Instance NullabilityOfRet: U Availability: available AvailabilityMsg: '' + SwiftName: '' DesignatedInit: true - Selector: 'initImageCell:' MethodKind: Instance @@ -19,6 +21,7 @@ Classes: NullabilityOfRet: U Availability: available AvailabilityMsg: '' + SwiftName: '' DesignatedInit: true - Selector: 'initTextCell:' MethodKind: Instance @@ -26,6 +29,7 @@ Classes: NullabilityOfRet: U Availability: available AvailabilityMsg: '' + SwiftName: '' DesignatedInit: true - Selector: 'initWithCoder:' MethodKind: Instance @@ -33,12 +37,14 @@ Classes: NullabilityOfRet: U Availability: available AvailabilityMsg: '' + SwiftName: '' DesignatedInit: true Required: true - Name: NSView AuditedForNullability: true Availability: available AvailabilityMsg: '' + SwiftName: '' Methods: - Selector: 'addSubview:' MethodKind: Instance @@ -46,34 +52,41 @@ Classes: NullabilityOfRet: N Availability: available AvailabilityMsg: '' + SwiftName: '' - Selector: 'addSubview:positioned:relativeTo:' MethodKind: Instance Nullability: [ N, N, O ] NullabilityOfRet: N Availability: available AvailabilityMsg: '' + SwiftName: '' - Selector: 'beginDraggingSessionWithItems:event:source:' MethodKind: Instance Nullability: [ U, U, N ] NullabilityOfRet: N Availability: available AvailabilityMsg: '' + SwiftName: 'beginDragginSession(_:event:source:)' Properties: - Name: enclosingScrollView Nullability: O Availability: available AvailabilityMsg: '' + SwiftName: enclosing - Name: makeBackingLayer Nullability: N Availability: available AvailabilityMsg: '' + SwiftName: '' Functions: - Name: NSAvailableWindowDepths NullabilityOfRet: N Availability: available AvailabilityMsg: '' + SwiftName: 'availableWindowDepths()' Globals: - Name: NSCalibratedWhiteColorSpace Nullability: N Availability: available AvailabilityMsg: '' + SwiftName: calibratedWhite From d7d740f893b640ea3d512551fb8f7da7dc17b875 Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Wed, 24 Feb 2016 17:49:50 +0000 Subject: [PATCH 217/742] Objective-C: Add a size field to non-fragile category metadata. This is mainly for extensibility. Note that fragile category metadata, metadata for classes and protocols all have a size field. Initial patch was provided by Greg Parker. rdar://problem/24804226 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261756 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CGObjCMac.cpp | 8 +++++++- test/CodeGenObjC/metadata-class-properties.m | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index 82c49c7dc8c..0d641377ca4 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/lib/CodeGen/CGObjCMac.cpp @@ -5587,6 +5587,7 @@ ObjCNonFragileABITypesHelper::ObjCNonFragileABITypesHelper(CodeGen::CodeGenModul // const struct _protocol_list_t * const protocols; // const struct _prop_list_t * const properties; // const struct _prop_list_t * const class_properties; + // const uint32_t size; // } CategorynfABITy = llvm::StructType::create("struct._category_t", Int8PtrTy, ClassnfABIPtrTy, @@ -5595,6 +5596,7 @@ ObjCNonFragileABITypesHelper::ObjCNonFragileABITypesHelper(CodeGen::CodeGenModul ProtocolListnfABIPtrTy, PropertyListPtrTy, PropertyListPtrTy, + IntTy, nullptr); // New types for nonfragile abi messaging. @@ -6156,6 +6158,7 @@ llvm::Value *CGObjCNonFragileABIMac::GenerateProtocolRef(CodeGenFunction &CGF, /// const struct _protocol_list_t * const protocols; /// const struct _prop_list_t * const properties; /// const struct _prop_list_t * const class_properties; +/// const uint32_t size; /// } /// void CGObjCNonFragileABIMac::GenerateCategory(const ObjCCategoryImplDecl *OCD) { @@ -6170,7 +6173,7 @@ void CGObjCNonFragileABIMac::GenerateCategory(const ObjCCategoryImplDecl *OCD) { llvm::SmallString<64> ExtClassName(getClassSymbolPrefix()); ExtClassName += Interface->getObjCRuntimeNameAsString(); - llvm::Constant *Values[7]; + llvm::Constant *Values[8]; Values[0] = GetClassName(OCD->getIdentifier()->getName()); // meta-class entry symbol llvm::Constant *ClassGV = GetClassGlobal(ExtClassName.str(), @@ -6229,6 +6232,9 @@ void CGObjCNonFragileABIMac::GenerateCategory(const ObjCCategoryImplDecl *OCD) { Values[6] = llvm::Constant::getNullValue(ObjCTypes.PropertyListPtrTy); } + unsigned Size = CGM.getDataLayout().getTypeAllocSize(ObjCTypes.CategorynfABITy); + Values[7] = llvm::ConstantInt::get(ObjCTypes.IntTy, Size); + llvm::Constant *Init = llvm::ConstantStruct::get(ObjCTypes.CategorynfABITy, Values); diff --git a/test/CodeGenObjC/metadata-class-properties.m b/test/CodeGenObjC/metadata-class-properties.m index aa5ed02ebe1..2def20025c9 100644 --- a/test/CodeGenObjC/metadata-class-properties.m +++ b/test/CodeGenObjC/metadata-class-properties.m @@ -4,7 +4,7 @@ // CHECK: @"\01l_OBJC_$_CLASS_PROP_LIST_Proto" = private global {{.*}} section "__DATA, __objc_const", align 8 // CHECK: @"\01l_OBJC_PROTOCOL_$_Proto" = {{.*}} global %struct._protocol_t { {{.*}} i32 96, i32 {{.*}} @"\01l_OBJC_$_CLASS_PROP_LIST_Proto" {{.*}} } // CHECK: @"\01l_OBJC_$_CLASS_PROP_LIST_Foo_$_Category" = private global {{.*}} section "__DATA, __objc_const", align 8 -// CHECK: @"\01l_OBJC_$_CATEGORY_Foo_$_Category" = private global %struct._category_t { {{.*}} @"\01l_OBJC_$_CLASS_PROP_LIST_Foo_$_Category" {{.*}} }, section "__DATA, __objc_const", align 8 +// CHECK: @"\01l_OBJC_$_CATEGORY_Foo_$_Category" = private global %struct._category_t { {{.*}} @"\01l_OBJC_$_CLASS_PROP_LIST_Foo_$_Category" {{.*}}, i32 64 }, section "__DATA, __objc_const", align 8 // CHECK: @"\01l_OBJC_$_CLASS_PROP_LIST_C" = private global {{.*}} section "__DATA, __objc_const", align 8 // CHECK: @"\01l_OBJC_METACLASS_RO_$_C" = private global %struct._class_ro_t { {{.*}} @"\01l_OBJC_$_CLASS_PROP_LIST_C" {{.*}} }, section "__DATA, __objc_const", align 8 From 4f47b953807ac66b8fd536695f564fb812c75609 Mon Sep 17 00:00:00 2001 From: Tim Northover Date: Wed, 24 Feb 2016 17:57:48 +0000 Subject: [PATCH 218/742] AArch64: fix Cyclone CPU features list. It turns out we don't have CRC after all. Who knew? git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261758 91177308-0d34-0410-b5e6-96231b3b80d8 Conflicts: lib/Driver/Tools.cpp test/Preprocessor/aarch64-target-features.c Just conflicting with addition of "kryo" CPU. --- lib/Driver/Tools.cpp | 7 +++++-- test/Preprocessor/aarch64-target-features.c | 6 +++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index 6e496b8d3ef..2106b30ecd1 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -2117,11 +2117,14 @@ static bool DecodeAArch64Mcpu(const Driver &D, StringRef Mcpu, StringRef &CPU, std::vector &Features) { std::pair Split = Mcpu.split("+"); CPU = Split.first; - if (CPU == "cyclone" || CPU == "cortex-a53" || CPU == "cortex-a57" || - CPU == "cortex-a72" || CPU == "cortex-a35" || CPU == "exynos-m1") { + if (CPU == "cortex-a53" || CPU == "cortex-a57" || CPU == "cortex-a72" || + CPU == "cortex-a35" || CPU == "exynos-m1") { Features.push_back("+neon"); Features.push_back("+crc"); Features.push_back("+crypto"); + } else if (CPU == "cyclone") { + Features.push_back("+neon"); + Features.push_back("+crypto"); } else if (CPU == "generic") { Features.push_back("+neon"); } else { diff --git a/test/Preprocessor/aarch64-target-features.c b/test/Preprocessor/aarch64-target-features.c index dbc29cff292..fca9cf643bc 100644 --- a/test/Preprocessor/aarch64-target-features.c +++ b/test/Preprocessor/aarch64-target-features.c @@ -93,7 +93,7 @@ // RUN: %clang -target aarch64 -mcpu=cortex-a57 -### -c %s 2>&1 | FileCheck -check-prefix=CHECK-MCPU-A57 %s // RUN: %clang -target aarch64 -mcpu=cortex-a72 -### -c %s 2>&1 | FileCheck -check-prefix=CHECK-MCPU-A72 %s // RUN: %clang -target aarch64 -mcpu=exynos-m1 -### -c %s 2>&1 | FileCheck -check-prefix=CHECK-MCPU-M1 %s -// CHECK-MCPU-CYCLONE: "-cc1"{{.*}} "-triple" "aarch64{{.*}}" "-target-feature" "+neon" "-target-feature" "+crc" "-target-feature" "+crypto" "-target-feature" "+zcm" "-target-feature" "+zcz" +// CHECK-MCPU-CYCLONE: "-cc1"{{.*}} "-triple" "aarch64{{.*}}" "-target-feature" "+neon" "-target-feature" "+crypto" "-target-feature" "+zcm" "-target-feature" "+zcz" // CHECK-MCPU-A35: "-cc1"{{.*}} "-triple" "aarch64{{.*}}" "-target-feature" "+neon" "-target-feature" "+crc" "-target-feature" "+crypto" // CHECK-MCPU-A53: "-cc1"{{.*}} "-triple" "aarch64{{.*}}" "-target-feature" "+neon" "-target-feature" "+crc" "-target-feature" "+crypto" // CHECK-MCPU-A57: "-cc1"{{.*}} "-triple" "aarch64{{.*}}" "-target-feature" "+neon" "-target-feature" "+crc" "-target-feature" "+crypto" @@ -101,7 +101,7 @@ // CHECK-MCPU-M1: "-cc1"{{.*}} "-triple" "aarch64{{.*}}" "-target-feature" "+neon" "-target-feature" "+crc" "-target-feature" "+crypto" // RUN: %clang -target x86_64-apple-macosx -arch arm64 -### -c %s 2>&1 | FileCheck --check-prefix=CHECK-ARCH-ARM64 %s -// CHECK-ARCH-ARM64: "-target-cpu" "cyclone" "-target-feature" "+neon" "-target-feature" "+crc" "-target-feature" "+crypto" "-target-feature" "+zcm" "-target-feature" "+zcz" +// CHECK-ARCH-ARM64: "-target-cpu" "cyclone" "-target-feature" "+neon" "-target-feature" "+crypto" "-target-feature" "+zcm" "-target-feature" "+zcz" // RUN: %clang -target aarch64 -march=armv8-a+fp+simd+crc+crypto -### -c %s 2>&1 | FileCheck -check-prefix=CHECK-MARCH-1 %s // RUN: %clang -target aarch64 -march=armv8-a+nofp+nosimd+nocrc+nocrypto+fp+simd+crc+crypto -### -c %s 2>&1 | FileCheck -check-prefix=CHECK-MARCH-1 %s @@ -123,7 +123,7 @@ // RUN: %clang -target aarch64 -mcpu=generic+Crc -### -c %s 2>&1 | FileCheck -check-prefix=CHECK-MCPU-2 %s // RUN: %clang -target aarch64 -mcpu=GENERIC+nocrc+CRC -### -c %s 2>&1 | FileCheck -check-prefix=CHECK-MCPU-2 %s // RUN: %clang -target aarch64 -mcpu=cortex-a53+noSIMD -### -c %s 2>&1 | FileCheck -check-prefix=CHECK-MCPU-3 %s -// CHECK-MCPU-1: "-cc1"{{.*}} "-triple" "aarch64{{.*}}" "-target-feature" "+neon" "-target-feature" "+crc" "-target-feature" "-crypto" "-target-feature" "+zcm" "-target-feature" "+zcz" +// CHECK-MCPU-1: "-cc1"{{.*}} "-triple" "aarch64{{.*}}" "-target-feature" "+neon" "-target-feature" "-crypto" "-target-feature" "+zcm" "-target-feature" "+zcz" // CHECK-MCPU-2: "-cc1"{{.*}} "-triple" "aarch64{{.*}}" "-target-feature" "+neon" "-target-feature" "+crc" // CHECK-MCPU-3: "-cc1"{{.*}} "-triple" "aarch64{{.*}}" "-target-feature" "-neon" From 9349f5f51eb116defa57afae1ead02035677be63 Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Wed, 24 Feb 2016 23:05:43 +0000 Subject: [PATCH 219/742] Fix assertion failure on MaybeODRUseExprs. In VisitNonTypeTemplateParamDecl, before SubstExpr with the default argument, we should create a ConstantEvaluated ExpressionEvaluationContext. Without this, it is possible to use a PotentiallyEvaluated ExpressionEvaluationContext; and MaybeODRUseExprs will not be cleared when popping the context, causing assertion failure. This is similar to how we handle the context before SubstExpr with the default argument, in SubstDefaultTemplateArgument. Part of PR13986. rdar://24480205 Differential Revision: http://reviews.llvm.org/D17576 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261803 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/SemaTemplateInstantiateDecl.cpp | 2 ++ test/SemaTemplate/default-arguments-cxx0x.cpp | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 7a452af7783..2c5a18c2b73 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2104,6 +2104,8 @@ Decl *TemplateDeclInstantiator::VisitNonTypeTemplateParmDecl( Param->setInvalidDecl(); if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) { + EnterExpressionEvaluationContext ConstantEvaluated(SemaRef, + Sema::ConstantEvaluated); ExprResult Value = SemaRef.SubstExpr(D->getDefaultArgument(), TemplateArgs); if (!Value.isInvalid()) Param->setDefaultArgument(Value.get()); diff --git a/test/SemaTemplate/default-arguments-cxx0x.cpp b/test/SemaTemplate/default-arguments-cxx0x.cpp index 0c97c2056b7..c52899a8e6d 100644 --- a/test/SemaTemplate/default-arguments-cxx0x.cpp +++ b/test/SemaTemplate/default-arguments-cxx0x.cpp @@ -75,3 +75,13 @@ namespace rdar23810407 { g(); } } + +// rdar://problem/24480205 +namespace PR13986 { + constexpr unsigned Dynamic = 0; + template class A { template void m_fn1(); }; + class Test { + ~Test() {} + A<1> m_target; + }; +} From 8e6511dfa956a99c3f4f85191a3a2994fc249484 Mon Sep 17 00:00:00 2001 From: Anton Yartsev Date: Sun, 21 Feb 2016 17:04:26 +0000 Subject: [PATCH 220/742] [analyzer][scan-build] Non-existing directory for scan-build output. Makes scan-build successfully accept non-existing output directories provided via "-o" option. The directory is created in this case. This behavior is conforming to the old perl scan-build implementation. (http://reviews.llvm.org/D17091) git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261480 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 8f351cf9bd30e6c8bc2577ff85410ed557ca1b51) --- tools/scan-build-py/libscanbuild/report.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/scan-build-py/libscanbuild/report.py b/tools/scan-build-py/libscanbuild/report.py index efc0a55de61..5c33319e206 100644 --- a/tools/scan-build-py/libscanbuild/report.py +++ b/tools/scan-build-py/libscanbuild/report.py @@ -35,7 +35,12 @@ def report_directory(hint, keep): keep -- a boolean value to keep or delete the empty report directory. """ stamp = time.strftime('scan-build-%Y-%m-%d-%H%M%S-', time.localtime()) - name = tempfile.mkdtemp(prefix=stamp, dir=hint) + + parentdir = os.path.abspath(hint) + if not os.path.exists(parentdir): + os.makedirs(parentdir) + + name = tempfile.mkdtemp(prefix=stamp, dir=parentdir) logging.info('Report directory created: %s', name) From 9000acfe76a9ff669230377b7b42a4915bc8c6d4 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Mon, 22 Feb 2016 17:56:24 +0000 Subject: [PATCH 221/742] [analyzer] Detect duplicate [super dealloc] calls Add an alpha path checker that warns about duplicate calls to [super dealloc]. This will form the foundation of a checker that will detect uses of 'self' after calling [super dealloc]. Part of rdar://problem/6953275. Based on a patch by David Kilzer! Differential Revision: http://reviews.llvm.org/D5238 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261545 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 2fcfab471cb98540560a07f5e86267cdc3a568b2) --- lib/StaticAnalyzer/Checkers/CMakeLists.txt | 1 + lib/StaticAnalyzer/Checkers/Checkers.td | 4 + .../Checkers/ObjCSuperDeallocChecker.cpp | 197 ++++++++++++ test/Analysis/DeallocUseAfterFreeErrors.m | 298 ++++++++++++++++++ 4 files changed, 500 insertions(+) create mode 100644 lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp create mode 100644 test/Analysis/DeallocUseAfterFreeErrors.m diff --git a/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/lib/StaticAnalyzer/Checkers/CMakeLists.txt index 58ff48d6a22..e1d98cf0d05 100644 --- a/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -56,6 +56,7 @@ add_clang_library(clangStaticAnalyzerCheckers ObjCContainersChecker.cpp ObjCMissingSuperCallChecker.cpp ObjCSelfInitChecker.cpp + ObjCSuperDeallocChecker.cpp ObjCUnusedIVarsChecker.cpp PaddingChecker.cpp PointerArithChecker.cpp diff --git a/lib/StaticAnalyzer/Checkers/Checkers.td b/lib/StaticAnalyzer/Checkers/Checkers.td index 8133d290d88..7748d9cbc83 100644 --- a/lib/StaticAnalyzer/Checkers/Checkers.td +++ b/lib/StaticAnalyzer/Checkers/Checkers.td @@ -513,6 +513,10 @@ def ObjCDeallocChecker : Checker<"Dealloc">, HelpText<"Warn about Objective-C classes that lack a correct implementation of -dealloc">, DescFile<"CheckObjCDealloc.cpp">; +def ObjCSuperDeallocChecker : Checker<"SuperDealloc">, + HelpText<"Warn about improper use of '[super dealloc]' in Objective-C">, + DescFile<"ObjCSuperDeallocChecker.cpp">; + def InstanceVariableInvalidation : Checker<"InstanceVariableInvalidation">, HelpText<"Check that the invalidatable instance variables are invalidated in the methods annotated with objc_instance_variable_invalidator">, DescFile<"IvarInvalidationChecker.cpp">; diff --git a/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp new file mode 100644 index 00000000000..d1d6ef388db --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp @@ -0,0 +1,197 @@ +//===- ObjCSuperDeallocChecker.cpp - Check correct use of [super dealloc] -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines ObjCSuperDeallocChecker, a builtin check that warns when +// [super dealloc] is called twice on the same instance in MRR mode. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" + +using namespace clang; +using namespace ento; + +namespace { +class ObjCSuperDeallocChecker + : public Checker { + + mutable IdentifierInfo *IIdealloc, *IINSObject; + mutable Selector SELdealloc; + + std::unique_ptr DoubleSuperDeallocBugType; + + void initIdentifierInfoAndSelectors(ASTContext &Ctx) const; + + bool isSuperDeallocMessage(const ObjCMethodCall &M) const; + +public: + ObjCSuperDeallocChecker(); + void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; + void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; +}; + +} // End anonymous namespace. + +// Remember whether [super dealloc] has previously been called on the +// a SymbolRef for the receiver. +REGISTER_SET_WITH_PROGRAMSTATE(CalledSuperDealloc, SymbolRef) + +class SuperDeallocBRVisitor final + : public BugReporterVisitorImpl { + + SymbolRef ReceiverSymbol; + bool Satisfied; + +public: + SuperDeallocBRVisitor(SymbolRef ReceiverSymbol) + : ReceiverSymbol(ReceiverSymbol), + Satisfied(false) {} + + PathDiagnosticPiece *VisitNode(const ExplodedNode *Succ, + const ExplodedNode *Pred, + BugReporterContext &BRC, + BugReport &BR) override; + + void Profile(llvm::FoldingSetNodeID &ID) const override { + ID.Add(ReceiverSymbol); + } +}; + +void ObjCSuperDeallocChecker::checkPreObjCMessage(const ObjCMethodCall &M, + CheckerContext &C) const { + if (!isSuperDeallocMessage(M)) + return; + + ProgramStateRef State = C.getState(); + SymbolRef ReceiverSymbol = M.getReceiverSVal().getAsSymbol(); + assert(ReceiverSymbol && "No receiver symbol at call to [super dealloc]?"); + + bool AlreadyCalled = State->contains(ReceiverSymbol); + + // If [super dealloc] has not been called, there is nothing to do. We'll + // note the fact that [super dealloc] was called in checkPostObjCMessage. + if (!AlreadyCalled) + return; + + // We have a duplicate [super dealloc] method call. + // This likely causes a crash, so stop exploring the + // path by generating a sink. + ExplodedNode *ErrNode = C.generateErrorNode(); + // If we've already reached this node on another path, return. + if (!ErrNode) + return; + + // Generate the report. + std::unique_ptr BR( + new BugReport(*DoubleSuperDeallocBugType, + "[super dealloc] should not be called multiple times", + ErrNode)); + BR->addRange(M.getOriginExpr()->getSourceRange()); + BR->addVisitor(llvm::make_unique(ReceiverSymbol)); + C.emitReport(std::move(BR)); + + return; +} + +void ObjCSuperDeallocChecker::checkPostObjCMessage(const ObjCMethodCall &M, + CheckerContext &C) const { + // Check for [super dealloc] method call. + if (!isSuperDeallocMessage(M)) + return; + + ProgramStateRef State = C.getState(); + SymbolRef ReceiverSymbol = M.getSelfSVal().getAsSymbol(); + assert(ReceiverSymbol && "No receiver symbol at call to [super dealloc]?"); + + // We add this transition in checkPostObjCMessage to avoid warning when + // we inline a call to [super dealloc] where the inlined call itself + // calls [super dealloc]. + State = State->add(ReceiverSymbol); + C.addTransition(State); +} + +ObjCSuperDeallocChecker::ObjCSuperDeallocChecker() + : IIdealloc(nullptr), IINSObject(nullptr) { + + DoubleSuperDeallocBugType.reset( + new BugType(this, "[super dealloc] should not be called more than once", + categories::CoreFoundationObjectiveC)); +} + +void +ObjCSuperDeallocChecker::initIdentifierInfoAndSelectors(ASTContext &Ctx) const { + if (IIdealloc) + return; + + IIdealloc = &Ctx.Idents.get("dealloc"); + IINSObject = &Ctx.Idents.get("NSObject"); + + SELdealloc = Ctx.Selectors.getSelector(0, &IIdealloc); +} + +bool +ObjCSuperDeallocChecker::isSuperDeallocMessage(const ObjCMethodCall &M) const { + if (M.getOriginExpr()->getReceiverKind() != ObjCMessageExpr::SuperInstance) + return false; + + ASTContext &Ctx = M.getState()->getStateManager().getContext(); + initIdentifierInfoAndSelectors(Ctx); + + return M.getSelector() == SELdealloc; +} + +PathDiagnosticPiece *SuperDeallocBRVisitor::VisitNode(const ExplodedNode *Succ, + const ExplodedNode *Pred, + BugReporterContext &BRC, + BugReport &BR) { + if (Satisfied) + return nullptr; + + ProgramStateRef State = Succ->getState(); + + bool CalledNow = + Succ->getState()->contains(ReceiverSymbol); + bool CalledBefore = + Pred->getState()->contains(ReceiverSymbol); + + // Is Succ the node on which the analyzer noted that [super dealloc] was + // called on ReceiverSymbol? + if (CalledNow && !CalledBefore) { + Satisfied = true; + + ProgramPoint P = Succ->getLocation(); + PathDiagnosticLocation L = + PathDiagnosticLocation::create(P, BRC.getSourceManager()); + + if (!L.isValid() || !L.asLocation().isValid()) + return nullptr; + + return new PathDiagnosticEventPiece( + L, "[super dealloc] called here"); + } + + return nullptr; +} + +//===----------------------------------------------------------------------===// +// Checker Registration. +//===----------------------------------------------------------------------===// + +void ento::registerObjCSuperDeallocChecker(CheckerManager &Mgr) { + const LangOptions &LangOpts = Mgr.getLangOpts(); + if (LangOpts.getGC() == LangOptions::GCOnly || LangOpts.ObjCAutoRefCount) + return; + Mgr.registerChecker(); +} diff --git a/test/Analysis/DeallocUseAfterFreeErrors.m b/test/Analysis/DeallocUseAfterFreeErrors.m new file mode 100644 index 00000000000..75c2a1e573f --- /dev/null +++ b/test/Analysis/DeallocUseAfterFreeErrors.m @@ -0,0 +1,298 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.osx.cocoa.SuperDealloc,debug.ExprInspection -analyzer-output=text -verify %s + +void clang_analyzer_warnIfReached(); + +#define nil ((id)0) + +typedef unsigned long NSUInteger; +@protocol NSObject +- (instancetype)retain; +- (oneway void)release; +@end + +@interface NSObject { } +- (void)dealloc; +- (instancetype)init; +@end + +typedef struct objc_selector *SEL; + +//===------------------------------------------------------------------------=== +// +// Check that 'self' is not referenced after calling '[super dealloc]'. + +@interface SuperDeallocThenReleaseIvarClass : NSObject { + NSObject *_ivar; +} +@end + +@implementation SuperDeallocThenReleaseIvarClass +- (instancetype)initWithIvar:(NSObject *)ivar { + self = [super init]; + if (!self) + return nil; + _ivar = [ivar retain]; + return self; +} +- (void)dealloc { + [super dealloc]; + [_ivar release]; +} +@end + +@interface SuperDeallocThenAssignNilToIvarClass : NSObject { + NSObject *_delegate; +} +@end + +@implementation SuperDeallocThenAssignNilToIvarClass +- (instancetype)initWithDelegate:(NSObject *)delegate { + self = [super init]; + if (!self) + return nil; + _delegate = delegate; + return self; +} +- (void)dealloc { + [super dealloc]; + _delegate = nil; +} +@end + +@interface SuperDeallocThenReleasePropertyClass : NSObject { } +@property (retain) NSObject *ivar; +@end + +@implementation SuperDeallocThenReleasePropertyClass +- (instancetype)initWithProperty:(NSObject *)ivar { + self = [super init]; + if (!self) + return nil; + self.ivar = ivar; + return self; +} +- (void)dealloc { + [super dealloc]; + self.ivar = nil; +} +@end + +@interface SuperDeallocThenAssignNilToPropertyClass : NSObject { } +@property (assign) NSObject *delegate; +@end + +@implementation SuperDeallocThenAssignNilToPropertyClass +- (instancetype)initWithDelegate:(NSObject *)delegate { + self = [super init]; + if (!self) + return nil; + self.delegate = delegate; + return self; +} +- (void)dealloc { + [super dealloc]; + self.delegate = nil; +} +@end + +@interface SuperDeallocThenCallInstanceMethodClass : NSObject { } +- (void)_invalidate; +@end + +@implementation SuperDeallocThenCallInstanceMethodClass +- (void)_invalidate { +} +- (void)dealloc { + [super dealloc]; + [self _invalidate]; +} +@end + +@interface SuperDeallocThenCallNonObjectiveCMethodClass : NSObject { } +@end + +static void _invalidate(NSObject *object) { + (void)object; +} + +@implementation SuperDeallocThenCallNonObjectiveCMethodClass +- (void)dealloc { + [super dealloc]; + _invalidate(self); +} +@end + +@interface TwoSuperDeallocCallsClass : NSObject { + NSObject *_ivar; +} +- (void)_invalidate; +@end + +@implementation TwoSuperDeallocCallsClass +- (void)_invalidate { +} +- (void)dealloc { + if (_ivar) { + [_ivar release]; + [super dealloc]; + return; + } + [super dealloc]; + [self _invalidate]; +} +@end + +//===------------------------------------------------------------------------=== +// Warn about calling [super dealloc] twice due to missing return statement. + +@interface MissingReturnCausesDoubleSuperDeallocClass : NSObject { + NSObject *_ivar; +} +@end + +@implementation MissingReturnCausesDoubleSuperDeallocClass +- (void)dealloc { + if (_ivar) { // expected-note {{Taking true branch}} + [_ivar release]; + [super dealloc]; // expected-note {{[super dealloc] called here}} + // return; + } + [super dealloc]; // expected-warning{{[super dealloc] should not be called multiple times}} + // expected-note@-1{{[super dealloc] should not be called multiple times}} +} +@end + +//===------------------------------------------------------------------------=== +// Warn about calling [super dealloc] twice in two different methods. + +@interface SuperDeallocInOtherMethodClass : NSObject { + NSObject *_ivar; +} +- (void)_cleanup; +@end + +@implementation SuperDeallocInOtherMethodClass +- (void)_cleanup { + [_ivar release]; + [super dealloc]; // expected-note {{[super dealloc] called here}} +} +- (void)dealloc { + [self _cleanup]; // expected-note {{Calling '_cleanup'}} + //expected-note@-1 {{Returning from '_cleanup'}} + [super dealloc]; // expected-warning {{[super dealloc] should not be called multiple times}} + // expected-note@-1 {{[super dealloc] should not be called multiple times}} +} +@end + +//===------------------------------------------------------------------------=== +// Do not warn about calling [super dealloc] recursively for different objects +// of the same type with custom retain counting. +// +// A class that contains an ivar of itself with custom retain counting (such +// as provided by _OBJC_SUPPORTED_INLINE_REFCNT_WITH_DEALLOC2MAIN) can generate +// a false positive that [super dealloc] is called twice if each object instance +// is not tracked separately by the checker. This test case is just a simple +// approximation to trigger the false positive. + +@class ClassWithOwnIvarInstanceClass; +@interface ClassWithOwnIvarInstanceClass : NSObject { + ClassWithOwnIvarInstanceClass *_ivar; + NSUInteger _retainCount; +} +@end + +@implementation ClassWithOwnIvarInstanceClass +- (instancetype)retain { + ++_retainCount; + return self; +} +- (oneway void)release { + --_retainCount; + if (!_retainCount) + [self dealloc]; +} +- (void)dealloc { + [_ivar release]; + [super dealloc]; // no warning: different instances of same class +} +@end + +//===------------------------------------------------------------------------=== +// Do not warn about calling [super dealloc] twice if +dealloc is a class +// method. + +@interface SuperDeallocClassMethodIgnoredClass : NSObject { } ++ (void)dealloc; +@end + +@implementation SuperDeallocClassMethodIgnoredClass ++ (void)dealloc { } +@end + +@interface SuperDeallocClassMethodIgnoredSubClass : NSObject { } ++ (void)dealloc; +@end + +@implementation SuperDeallocClassMethodIgnoredSubClass ++ (void)dealloc { + [super dealloc]; + [super dealloc]; // no warning: class method +} +@end + +//===------------------------------------------------------------------------=== +// Do not warn about calling [super dealloc] twice if when the analyzer has +// inlined the call to its super deallocator. + +@interface SuperClassCallingSuperDealloc : NSObject +@end + +@implementation SuperClassCallingSuperDealloc +- (void)dealloc; { + [super dealloc]; +} +@end + +@interface SubclassCallingSuperDealloc : SuperClassCallingSuperDealloc +@end + +@implementation SubclassCallingSuperDealloc +- (void)dealloc; { + [super dealloc]; +} +@end + +//===------------------------------------------------------------------------=== +// Treat calling [super dealloc] twice as as a sink. + +@interface CallingSuperDeallocTwiceIsSink : NSObject +@end + +@implementation CallingSuperDeallocTwiceIsSink +- (void)dealloc; { + [super dealloc]; // expected-note {{[super dealloc] called here}} + [super dealloc]; // expected-warning {{[super dealloc] should not be called multiple times}} + // expected-note@-1 {{[super dealloc] should not be called multiple times}} + + clang_analyzer_warnIfReached(); // no-warning +} +@end + + +//===------------------------------------------------------------------------=== +// Test path notes with intervening method call on self. + +@interface InterveningMethodCallOnSelf : NSObject +@end + +@implementation InterveningMethodCallOnSelf +- (void)anotherMethod { +} + +- (void)dealloc; { + [super dealloc]; // expected-note {{[super dealloc] called here}} + [self anotherMethod]; + [super dealloc]; // expected-warning {{[super dealloc] should not be called multiple times}} + // expected-note@-1 {{[super dealloc] should not be called multiple times}} +} +@end From 444987001880f5826edee2c8a7d10ac0717c8397 Mon Sep 17 00:00:00 2001 From: Gabor Horvath Date: Tue, 23 Feb 2016 12:34:39 +0000 Subject: [PATCH 222/742] [analyzer] Improve pointer arithmetic checker. This patch is intended to improve pointer arithmetic checker. From now on it only warns when the pointer arithmetic is likely to cause an error. For example when the pointer points to a single object, or an array of derived types. Differential Revision: http://reviews.llvm.org/D14203 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261632 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit e17e2519c1697b449e3e2c7b4746bc0f786505ca) --- .../Checkers/PointerArithChecker.cpp | 324 ++++++++++++++++-- test/Analysis/PR24184.cpp | 4 +- test/Analysis/fields.c | 2 +- test/Analysis/ptr-arith.c | 4 +- test/Analysis/ptr-arith.cpp | 82 ++++- test/Analysis/rdar-6442306-1.m | 2 +- 6 files changed, 385 insertions(+), 33 deletions(-) diff --git a/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp b/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp index e3369677af7..df5118806bf 100644 --- a/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp @@ -13,55 +13,329 @@ //===----------------------------------------------------------------------===// #include "ClangSACheckers.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/ExprCXX.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "llvm/ADT/SmallVector.h" using namespace clang; using namespace ento; +namespace { +enum class AllocKind { + SingleObject, + Array, + Unknown, + Reinterpreted // Single object interpreted as an array. +}; +} // end namespace + +namespace llvm { +template <> struct FoldingSetTrait { + static inline void Profile(AllocKind X, FoldingSetNodeID &ID) { + ID.AddInteger(static_cast(X)); + } +}; +} // end namespace llvm + namespace { class PointerArithChecker - : public Checker< check::PreStmt > { - mutable std::unique_ptr BT; + : public Checker< + check::PreStmt, check::PreStmt, + check::PreStmt, check::PreStmt, + check::PostStmt, check::PostStmt, + check::PostStmt, check::DeadSymbols> { + AllocKind getKindOfNewOp(const CXXNewExpr *NE, const FunctionDecl *FD) const; + const MemRegion *getArrayRegion(const MemRegion *Region, bool &Polymorphic, + AllocKind &AKind, CheckerContext &C) const; + const MemRegion *getPointedRegion(const MemRegion *Region, + CheckerContext &C) const; + void reportPointerArithMisuse(const Expr *E, CheckerContext &C, + bool PointedNeeded = false) const; + void initAllocIdentifiers(ASTContext &C) const; + + mutable std::unique_ptr BT_pointerArith; + mutable std::unique_ptr BT_polyArray; + mutable llvm::SmallSet AllocFunctions; public: - void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const; + void checkPreStmt(const UnaryOperator *UOp, CheckerContext &C) const; + void checkPreStmt(const BinaryOperator *BOp, CheckerContext &C) const; + void checkPreStmt(const ArraySubscriptExpr *SubExpr, CheckerContext &C) const; + void checkPreStmt(const CastExpr *CE, CheckerContext &C) const; + void checkPostStmt(const CastExpr *CE, CheckerContext &C) const; + void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const; + void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; + void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; }; +} // end namespace + +REGISTER_MAP_WITH_PROGRAMSTATE(RegionState, const MemRegion *, AllocKind) + +void PointerArithChecker::checkDeadSymbols(SymbolReaper &SR, + CheckerContext &C) const { + // TODO: intentional leak. Some information is garbage collected too early, + // see http://reviews.llvm.org/D14203 for further information. + /*ProgramStateRef State = C.getState(); + RegionStateTy RegionStates = State->get(); + for (RegionStateTy::iterator I = RegionStates.begin(), E = RegionStates.end(); + I != E; ++I) { + if (!SR.isLiveRegion(I->first)) + State = State->remove(I->first); + } + C.addTransition(State);*/ } -void PointerArithChecker::checkPreStmt(const BinaryOperator *B, - CheckerContext &C) const { - if (B->getOpcode() != BO_Sub && B->getOpcode() != BO_Add) - return; +AllocKind PointerArithChecker::getKindOfNewOp(const CXXNewExpr *NE, + const FunctionDecl *FD) const { + // This checker try not to assume anything about placement and overloaded + // new to avoid false positives. + if (isa(FD)) + return AllocKind::Unknown; + if (FD->getNumParams() != 1 || FD->isVariadic()) + return AllocKind::Unknown; + if (NE->isArray()) + return AllocKind::Array; + + return AllocKind::SingleObject; +} + +const MemRegion * +PointerArithChecker::getPointedRegion(const MemRegion *Region, + CheckerContext &C) const { + assert(Region); + ProgramStateRef State = C.getState(); + SVal S = State->getSVal(Region); + return S.getAsRegion(); +} - ProgramStateRef state = C.getState(); - const LocationContext *LCtx = C.getLocationContext(); - SVal LV = state->getSVal(B->getLHS(), LCtx); - SVal RV = state->getSVal(B->getRHS(), LCtx); +/// Checks whether a region is the part of an array. +/// In case there is a dericed to base cast above the array element, the +/// Polymorphic output value is set to true. AKind output value is set to the +/// allocation kind of the inspected region. +const MemRegion *PointerArithChecker::getArrayRegion(const MemRegion *Region, + bool &Polymorphic, + AllocKind &AKind, + CheckerContext &C) const { + assert(Region); + while (Region->getKind() == MemRegion::Kind::CXXBaseObjectRegionKind) { + Region = Region->getAs()->getSuperRegion(); + Polymorphic = true; + } + if (Region->getKind() == MemRegion::Kind::ElementRegionKind) { + Region = Region->getAs()->getSuperRegion(); + } - const MemRegion *LR = LV.getAsRegion(); + ProgramStateRef State = C.getState(); + if (const AllocKind *Kind = State->get(Region)) { + AKind = *Kind; + if (*Kind == AllocKind::Array) + return Region; + else + return nullptr; + } + // When the region is symbolic and we do not have any information about it, + // assume that this is an array to avoid false positives. + if (Region->getKind() == MemRegion::Kind::SymbolicRegionKind) + return Region; - if (!LR || !RV.isConstant()) + // No AllocKind stored and not symbolic, assume that it points to a single + // object. + return nullptr; +} + +void PointerArithChecker::reportPointerArithMisuse(const Expr *E, + CheckerContext &C, + bool PointedNeeded) const { + SourceRange SR = E->getSourceRange(); + if (SR.isInvalid()) return; - // If pointer arithmetic is done on variables of non-array type, this often - // means behavior rely on memory organization, which is dangerous. - if (isa(LR) || isa(LR) || - isa(LR)) { + ProgramStateRef State = C.getState(); + const MemRegion *Region = + State->getSVal(E, C.getLocationContext()).getAsRegion(); + if (!Region) + return; + if (PointedNeeded) + Region = getPointedRegion(Region, C); + if (!Region) + return; + bool IsPolymorphic = false; + AllocKind Kind = AllocKind::Unknown; + if (const MemRegion *ArrayRegion = + getArrayRegion(Region, IsPolymorphic, Kind, C)) { + if (!IsPolymorphic) + return; if (ExplodedNode *N = C.generateNonFatalErrorNode()) { - if (!BT) - BT.reset( - new BuiltinBug(this, "Dangerous pointer arithmetic", - "Pointer arithmetic done on non-array variables " - "means reliance on memory layout, which is " - "dangerous.")); - auto R = llvm::make_unique(*BT, BT->getDescription(), N); - R->addRange(B->getSourceRange()); + if (!BT_polyArray) + BT_polyArray.reset(new BuiltinBug( + this, "Dangerous pointer arithmetic", + "Pointer arithmetic on a pointer to base class is dangerous " + "because derived and base class may have different size.")); + auto R = llvm::make_unique(*BT_polyArray, + BT_polyArray->getDescription(), N); + R->addRange(E->getSourceRange()); + R->markInteresting(ArrayRegion); C.emitReport(std::move(R)); } + return; + } + + if (Kind == AllocKind::Reinterpreted) + return; + + // We might not have enough information about symbolic regions. + if (Kind != AllocKind::SingleObject && + Region->getKind() == MemRegion::Kind::SymbolicRegionKind) + return; + + if (ExplodedNode *N = C.generateNonFatalErrorNode()) { + if (!BT_pointerArith) + BT_pointerArith.reset(new BuiltinBug(this, "Dangerous pointer arithmetic", + "Pointer arithmetic on non-array " + "variables relies on memory layout, " + "which is dangerous.")); + auto R = llvm::make_unique(*BT_pointerArith, + BT_pointerArith->getDescription(), N); + R->addRange(SR); + R->markInteresting(Region); + C.emitReport(std::move(R)); + } +} + +void PointerArithChecker::initAllocIdentifiers(ASTContext &C) const { + if (!AllocFunctions.empty()) + return; + AllocFunctions.insert(&C.Idents.get("alloca")); + AllocFunctions.insert(&C.Idents.get("malloc")); + AllocFunctions.insert(&C.Idents.get("realloc")); + AllocFunctions.insert(&C.Idents.get("calloc")); + AllocFunctions.insert(&C.Idents.get("valloc")); +} + +void PointerArithChecker::checkPostStmt(const CallExpr *CE, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + const FunctionDecl *FD = C.getCalleeDecl(CE); + if (!FD) + return; + IdentifierInfo *FunI = FD->getIdentifier(); + initAllocIdentifiers(C.getASTContext()); + if (AllocFunctions.count(FunI) == 0) + return; + + SVal SV = State->getSVal(CE, C.getLocationContext()); + const MemRegion *Region = SV.getAsRegion(); + if (!Region) + return; + // Assume that C allocation functions allocate arrays to avoid false + // positives. + // TODO: Add heuristics to distinguish alloc calls that allocates single + // objecs. + State = State->set(Region, AllocKind::Array); + C.addTransition(State); +} + +void PointerArithChecker::checkPostStmt(const CXXNewExpr *NE, + CheckerContext &C) const { + const FunctionDecl *FD = NE->getOperatorNew(); + if (!FD) + return; + + AllocKind Kind = getKindOfNewOp(NE, FD); + + ProgramStateRef State = C.getState(); + SVal AllocedVal = State->getSVal(NE, C.getLocationContext()); + const MemRegion *Region = AllocedVal.getAsRegion(); + if (!Region) + return; + State = State->set(Region, Kind); + C.addTransition(State); +} + +void PointerArithChecker::checkPostStmt(const CastExpr *CE, + CheckerContext &C) const { + if (CE->getCastKind() != CastKind::CK_BitCast) + return; + + const Expr *CastedExpr = CE->getSubExpr(); + ProgramStateRef State = C.getState(); + SVal CastedVal = State->getSVal(CastedExpr, C.getLocationContext()); + + const MemRegion *Region = CastedVal.getAsRegion(); + if (!Region) + return; + + // Suppress reinterpret casted hits. + State = State->set(Region, AllocKind::Reinterpreted); + C.addTransition(State); +} + +void PointerArithChecker::checkPreStmt(const CastExpr *CE, + CheckerContext &C) const { + if (CE->getCastKind() != CastKind::CK_ArrayToPointerDecay) + return; + + const Expr *CastedExpr = CE->getSubExpr(); + ProgramStateRef State = C.getState(); + SVal CastedVal = State->getSVal(CastedExpr, C.getLocationContext()); + + const MemRegion *Region = CastedVal.getAsRegion(); + if (!Region) + return; + + if (const AllocKind *Kind = State->get(Region)) { + if (*Kind == AllocKind::Array || *Kind == AllocKind::Reinterpreted) + return; + } + State = State->set(Region, AllocKind::Array); + C.addTransition(State); +} + +void PointerArithChecker::checkPreStmt(const UnaryOperator *UOp, + CheckerContext &C) const { + if (!UOp->isIncrementDecrementOp() || !UOp->getType()->isPointerType()) + return; + reportPointerArithMisuse(UOp->getSubExpr(), C, true); +} + +void PointerArithChecker::checkPreStmt(const ArraySubscriptExpr *SubsExpr, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SVal Idx = State->getSVal(SubsExpr->getIdx(), C.getLocationContext()); + + // Indexing with 0 is OK. + if (Idx.isZeroConstant()) + return; + reportPointerArithMisuse(SubsExpr->getBase(), C); +} + +void PointerArithChecker::checkPreStmt(const BinaryOperator *BOp, + CheckerContext &C) const { + BinaryOperatorKind OpKind = BOp->getOpcode(); + if (!BOp->isAdditiveOp() && OpKind != BO_AddAssign && OpKind != BO_SubAssign) + return; + + const Expr *Lhs = BOp->getLHS(); + const Expr *Rhs = BOp->getRHS(); + ProgramStateRef State = C.getState(); + + if (Rhs->getType()->isIntegerType() && Lhs->getType()->isPointerType()) { + SVal RHSVal = State->getSVal(Rhs, C.getLocationContext()); + if (State->isNull(RHSVal).isConstrainedTrue()) + return; + reportPointerArithMisuse(Lhs, C, !BOp->isAdditiveOp()); + } + // The int += ptr; case is not valid C++. + if (Lhs->getType()->isIntegerType() && Rhs->getType()->isPointerType()) { + SVal LHSVal = State->getSVal(Lhs, C.getLocationContext()); + if (State->isNull(LHSVal).isConstrainedTrue()) + return; + reportPointerArithMisuse(Rhs, C); } } diff --git a/test/Analysis/PR24184.cpp b/test/Analysis/PR24184.cpp index db0df6f9c39..54eae563e7d 100644 --- a/test/Analysis/PR24184.cpp +++ b/test/Analysis/PR24184.cpp @@ -12,7 +12,7 @@ int a; typedef int *vcreate_t(int *, DATA_TYPE, int, int); void fn1(unsigned, unsigned) { char b = 0; - for (; 1; a++, &b + a * 0) // expected-warning{{Pointer arithmetic done on non-array variables means reliance on memory layout, which is dangerous}} + for (; 1; a++, &b + a * 0) ; } @@ -55,7 +55,7 @@ void fn1_1(void *p1, const void *p2) { p1 != p2; } void fn2_1(uint32_t *p1, unsigned char *p2, uint32_t p3) { unsigned i = 0; for (0; i < p3; i++) - fn1_1(p1 + i, p2 + i * 0); // expected-warning{{Pointer arithmetic done on non-array variables means reliance on memory layout, which is dangerous}} + fn1_1(p1 + i, p2 + i * 0); } struct A_1 { diff --git a/test/Analysis/fields.c b/test/Analysis/fields.c index 863a21aaf61..a670c50434e 100644 --- a/test/Analysis/fields.c +++ b/test/Analysis/fields.c @@ -16,7 +16,7 @@ struct s { void f() { struct s a; - int *p = &(a.n) + 1; + int *p = &(a.n) + 1; // expected-warning{{Pointer arithmetic on}} } typedef struct { diff --git a/test/Analysis/ptr-arith.c b/test/Analysis/ptr-arith.c index 57463cc7c87..2b15badf427 100644 --- a/test/Analysis/ptr-arith.c +++ b/test/Analysis/ptr-arith.c @@ -52,7 +52,7 @@ void f4() { void f5() { int x, y; int *p; - p = &x + 1; // expected-warning{{Pointer arithmetic done on non-array variables means reliance on memory layout, which is dangerous}} + p = &x + 1; // expected-warning{{Pointer arithmetic on non-array variables relies on memory layout, which is dangerous}} int a[10]; p = a + 1; // no-warning @@ -75,7 +75,7 @@ void null_operand(int *a) { clang_analyzer_eval(&a != 0); // expected-warning{{TRUE}} clang_analyzer_eval(&a >= 0); // expected-warning{{TRUE}} clang_analyzer_eval(&a > 0); // expected-warning{{TRUE}} - clang_analyzer_eval((&a - 0) != 0); // expected-warning{{TRUE}} expected-warning{{Pointer arithmetic done on non-array variables}} + clang_analyzer_eval((&a - 0) != 0); // expected-warning{{TRUE}} // LHS is NULL, RHS is non-symbolic // The same code is used for labels and non-symbolic values. diff --git a/test/Analysis/ptr-arith.cpp b/test/Analysis/ptr-arith.cpp index 5f0951857b1..07ddec30af5 100644 --- a/test/Analysis/ptr-arith.cpp +++ b/test/Analysis/ptr-arith.cpp @@ -1,5 +1,4 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -verify %s -// expected-no-diagnostics +// RUN: %clang_cc1 -Wno-unused-value -std=c++14 -analyze -analyzer-checker=core,debug.ExprInspection,alpha.core.PointerArithm -verify %s struct X { int *p; int zero; @@ -20,3 +19,82 @@ int test (int *in) { return 5/littleX.zero; // no-warning } + +class Base {}; +class Derived : public Base {}; + +void checkPolymorphicUse() { + Derived d[10]; + + Base *p = d; + ++p; // expected-warning{{Pointer arithmetic on a pointer to base class is dangerous}} +} + +void checkBitCasts() { + long l; + char *p = (char*)&l; + p = p+2; +} + +void checkBasicarithmetic(int i) { + int t[10]; + int *p = t; + ++p; + int a = 5; + p = &a; + ++p; // expected-warning{{Pointer arithmetic on non-array variables relies on memory layout, which is dangerous}} + p = p + 2; // expected-warning{{}} + p = 2 + p; // expected-warning{{}} + p += 2; // expected-warning{{}} + a += p[2]; // expected-warning{{}} + p = i*0 + p; + p = p + i*0; + p += i*0; +} + +void checkArithOnSymbolic(int*p) { + ++p; + p = p + 2; + p = 2 + p; + p += 2; + (void)p[2]; +} + +struct S { + int t[10]; +}; + +void arrayInStruct() { + S s; + int * p = s.t; + ++p; + S *sp = new S; + p = sp->t; + ++p; + delete sp; +} + +void checkNew() { + int *p = new int; + p[1] = 1; // expected-warning{{}} +} + +void InitState(int* state) { + state[1] = 1; // expected-warning{{}} +} + +int* getArray(int size) { + if (size == 0) + return new int; + return new int[5]; +} + +void checkConditionalArray() { + int* maybeArray = getArray(0); + InitState(maybeArray); +} + +void checkMultiDimansionalArray() { + int a[5][5]; + *(*(a+1)+2) = 2; +} diff --git a/test/Analysis/rdar-6442306-1.m b/test/Analysis/rdar-6442306-1.m index 0fb49c2a9b2..31a300c1f66 100644 --- a/test/Analysis/rdar-6442306-1.m +++ b/test/Analysis/rdar-6442306-1.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.core %s -analyzer-store=region -verify +// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.core -analyzer-disable-checker=alpha.core.PointerArithm %s -analyzer-store=region -verify // expected-no-diagnostics typedef int bar_return_t; From e1bccf2d1559536ac45b018a4d744e630a2d5b6a Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Tue, 23 Feb 2016 22:26:04 +0000 Subject: [PATCH 223/742] [analyzer] Find ObjC 'self' decl even when block captures local named 'self'. When looking up the 'self' decl in block captures, make sure to find the actual self declaration even when the block captures a local variable named 'self'. rdar://problem/24751280 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261703 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit a02dd11cedf26a6484297e5fa345685036672e4c) --- lib/Analysis/AnalysisDeclContext.cpp | 8 ++++++-- test/Analysis/blocks.m | 22 ++++++++++++++++++++++ test/Analysis/lambdas.mm | 15 ++++++++++++++- 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/lib/Analysis/AnalysisDeclContext.cpp b/lib/Analysis/AnalysisDeclContext.cpp index 94f753ed912..fe8021b7966 100644 --- a/lib/Analysis/AnalysisDeclContext.cpp +++ b/lib/Analysis/AnalysisDeclContext.cpp @@ -135,6 +135,10 @@ bool AnalysisDeclContext::isBodyAutosynthesizedFromModelFile() const { return Tmp && Body->getLocStart().isValid(); } +/// Returns true if \param VD is an Objective-C implicit 'self' parameter. +static bool isSelfDecl(const VarDecl *VD) { + return isa(VD) && VD->getName() == "self"; +} const ImplicitParamDecl *AnalysisDeclContext::getSelfDecl() const { if (const ObjCMethodDecl *MD = dyn_cast(D)) @@ -143,7 +147,7 @@ const ImplicitParamDecl *AnalysisDeclContext::getSelfDecl() const { // See if 'self' was captured by the block. for (const auto &I : BD->captures()) { const VarDecl *VD = I.getVariable(); - if (VD->getName() == "self") + if (isSelfDecl(VD)) return dyn_cast(VD); } } @@ -161,7 +165,7 @@ const ImplicitParamDecl *AnalysisDeclContext::getSelfDecl() const { continue; VarDecl *VD = LC.getCapturedVar(); - if (VD->getName() == "self") + if (isSelfDecl(VD)) return dyn_cast(VD); } diff --git a/test/Analysis/blocks.m b/test/Analysis/blocks.m index 4dbe9517207..0b1c15abb3b 100644 --- a/test/Analysis/blocks.m +++ b/test/Analysis/blocks.m @@ -210,3 +210,25 @@ void testCallContainingWithSignature5() }); } +__attribute__((objc_root_class)) +@interface SuperClass +- (void)someMethod; +@end + +@interface SomeClass : SuperClass +@end + +// Make sure to properly handle super-calls when a block captures +// a local variable named 'self'. +@implementation SomeClass +-(void)foo; { + /*__weak*/ SomeClass *weakSelf = self; + (void)(^(void) { + SomeClass *self = weakSelf; + (void)(^(void) { + (void)self; + [super someMethod]; // no-warning + }); + }); +} +@end diff --git a/test/Analysis/lambdas.mm b/test/Analysis/lambdas.mm index 6247f28870f..dc1a13e8b69 100644 --- a/test/Analysis/lambdas.mm +++ b/test/Analysis/lambdas.mm @@ -12,7 +12,6 @@ @interface Sub : Super { } @end - @implementation Sub - (void)callMethodOnSuperInCXXLambda; { // Explicit capture. @@ -26,6 +25,20 @@ - (void)callMethodOnSuperInCXXLambda; { }(); } +// Make sure to properly handle super-calls when a block captures +// a local variable named 'self'. +- (void)callMethodOnSuperInCXXLambdaWithRedefinedSelf; { + /*__weak*/ Sub *weakSelf = self; + // Implicit capture. (Sema outlaws explicit capture of a redefined self + // and a call to super [which uses the original self]). + [=]() { + Sub *self = weakSelf; + [=]() { + [super superMethod]; + }(); + }(); +} + - (void)swapIvars { int tmp = _ivar1; _ivar1 = _ivar2; From 50e616a7438800e22b07f9b3e0ab6538356762fe Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Thu, 25 Feb 2016 07:08:33 +0000 Subject: [PATCH 224/742] [Sema] Remove assert in TreeTransform::TransformObjCObjectType. The assert isn't correct since TypeLoc::ObjCObjectTypeLoc doesn't indicate whether the type is a dependent type. The function returns false for a type like "" which is a synonym for "id". rdar://problem/23838912 Differential Revision: http://reviews.llvm.org/D17355 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261829 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 23b19006a010647fee8db67a4bf89c4a38091554) --- lib/Sema/TreeTransform.h | 1 - test/SemaObjCXX/base-type-as-written.mm | 9 +++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 test/SemaObjCXX/base-type-as-written.mm diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index 60c661c4547..2092b579a47 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -5931,7 +5931,6 @@ TreeTransform::TransformObjCObjectType(TypeLocBuilder &TLB, } ObjCObjectTypeLoc NewT = TLB.push(Result); - assert(TL.hasBaseTypeAsWritten() && "Can't be dependent"); NewT.setHasBaseTypeAsWritten(true); NewT.setTypeArgsLAngleLoc(TL.getTypeArgsLAngleLoc()); for (unsigned i = 0, n = TL.getNumTypeArgs(); i != n; ++i) diff --git a/test/SemaObjCXX/base-type-as-written.mm b/test/SemaObjCXX/base-type-as-written.mm new file mode 100644 index 00000000000..05962e3f288 --- /dev/null +++ b/test/SemaObjCXX/base-type-as-written.mm @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +// Make sure we don't crash in TreeTransform::TransformObjCObjectType. + +@protocol P1 +@end + +template foo1(T1) { // expected-warning {{protocol has no object type specified; defaults to qualified 'id'}} + foo1(0); +} From 1b4af6c0afd0af3ec6f5ba9c74edd651b0893683 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Thu, 25 Feb 2016 18:55:24 +0000 Subject: [PATCH 225/742] [analyzer] Make ObjCDeallocChecker path sensitive. Convert the ObjCDeallocChecker to be path sensitive. The primary motivation for this change is to prevent false positives when -dealloc calls helper invalidation methods to release instance variables, but it additionally improves precision when -dealloc contains control flow. It also reduces the need for pattern matching. The check for missing -dealloc methods remains AST-based. Part of rdar://problem/6927496 Differential Revision: http://reviews.llvm.org/D17511 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261917 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit c1a5cdd6f421aca1284f1ed208e1da191bdba02c) --- .../Checkers/CheckObjCDealloc.cpp | 923 ++++++++++++++---- test/Analysis/DeallocMissingRelease.m | 476 ++++++++- test/Analysis/MissingDealloc.m | 14 + test/Analysis/PR2978.m | 70 +- test/Analysis/properties.m | 13 +- 5 files changed, 1282 insertions(+), 214 deletions(-) diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index 902babfe502..7b1302aea3e 100644 --- a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -7,9 +7,24 @@ // //===----------------------------------------------------------------------===// // -// This file defines a CheckObjCDealloc, a checker that -// analyzes an Objective-C class's implementation to determine if it -// correctly implements -dealloc. +// This checker analyzes Objective-C -dealloc methods and their callees +// to warn about improper releasing of instance variables that back synthesized +// properties. It warns about missing releases in the following cases: +// - When a class has a synthesized instance variable for a 'retain' or 'copy' +// property and lacks a -dealloc method in its implementation. +// - When a class has a synthesized instance variable for a 'retain'/'copy' +// property but the ivar is not released in -dealloc by either -release +// or by nilling out the property. +// +// It warns about extra releases in -dealloc (but not in callees) when a +// synthesized instance variable is released in the following cases: +// - When the property is 'assign' and is not 'readonly'. +// - When the property is 'weak'. +// +// This checker only warns for instance variables synthesized to back +// properties. Handling the more general case would require inferring whether +// an instance variable is stored retained or not. For synthesized properties, +// this is specified in the property declaration itself. // //===----------------------------------------------------------------------===// @@ -20,71 +35,36 @@ #include "clang/AST/ExprObjC.h" #include "clang/Basic/LangOptions.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" #include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; -// FIXME: This was taken from IvarInvalidationChecker.cpp -static const Expr *peel(const Expr *E) { - E = E->IgnoreParenCasts(); - if (const PseudoObjectExpr *POE = dyn_cast(E)) - E = POE->getSyntacticForm()->IgnoreParenCasts(); - if (const OpaqueValueExpr *OVE = dyn_cast(E)) - E = OVE->getSourceExpr()->IgnoreParenCasts(); - return E; -} +/// Indicates whether an instance variable is required to be released in +/// -dealloc. +enum class ReleaseRequirement { + /// The instance variable must be released, either by calling + /// -release on it directly or by nilling it out with a property setter. + MustRelease, -static bool scan_ivar_release(Stmt *S, const ObjCIvarDecl *ID, - const ObjCPropertyDecl *PD, - Selector Release, - IdentifierInfo* SelfII, - ASTContext &Ctx) { - - // [mMyIvar release] - if (ObjCMessageExpr *ME = dyn_cast(S)) - if (ME->getSelector() == Release) - if (ME->getInstanceReceiver()) - if (const Expr *Receiver = peel(ME->getInstanceReceiver())) - if (auto *E = dyn_cast(Receiver)) - if (E->getDecl() == ID) - return true; - - // [self setMyIvar:nil]; - if (ObjCMessageExpr *ME = dyn_cast(S)) - if (ME->getInstanceReceiver()) - if (const Expr *Receiver = peel(ME->getInstanceReceiver())) - if (auto *E = dyn_cast(Receiver)) - if (E->getDecl()->getIdentifier() == SelfII) - if (ME->getMethodDecl() == PD->getSetterMethodDecl() && - ME->getNumArgs() == 1 && - peel(ME->getArg(0))->isNullPointerConstant(Ctx, - Expr::NPC_ValueDependentIsNull)) - return true; - - // self.myIvar = nil; - if (BinaryOperator* BO = dyn_cast(S)) - if (BO->isAssignmentOp()) - if (auto *PRE = dyn_cast(peel(BO->getLHS()))) - if (PRE->isExplicitProperty() && PRE->getExplicitProperty() == PD) - if (peel(BO->getRHS())->isNullPointerConstant(Ctx, - Expr::NPC_ValueDependentIsNull)) { - // This is only a 'release' if the property kind is not - // 'assign'. - return PD->getSetterKind() != ObjCPropertyDecl::Assign; - } - - // Recurse to children. - for (Stmt *SubStmt : S->children()) - if (SubStmt && scan_ivar_release(SubStmt, ID, PD, Release, SelfII, Ctx)) - return true; + /// The instance variable must not be directly released with -release. + MustNotReleaseDirectly, - return false; -} + /// The requirement for the instance variable could not be determined. + Unknown +}; +/// Returns true if the property implementation is synthesized and the +/// type of the property is retainable. static bool isSynthesizedRetainableProperty(const ObjCPropertyImplDecl *I, const ObjCIvarDecl **ID, const ObjCPropertyDecl **PD) { @@ -107,33 +87,104 @@ static bool isSynthesizedRetainableProperty(const ObjCPropertyImplDecl *I, return true; } -static bool synthesizedPropertyRequiresRelease(const ObjCPropertyDecl *PD) { - // A synthesized property must be released if and only if the kind of setter - // was neither 'assign' or 'weak'. - ObjCPropertyDecl::SetterKind SK = PD->getSetterKind(); - return (SK != ObjCPropertyDecl::Assign && SK != ObjCPropertyDecl::Weak); -} +namespace { + +class ObjCDeallocChecker + : public Checker, + check::PreObjCMessage, check::PostObjCMessage, + check::BeginFunction, check::EndFunction, + check::PointerEscape, + check::PreStmt> { + + mutable IdentifierInfo *NSObjectII, *SenTestCaseII; + mutable Selector DeallocSel, ReleaseSel; + + std::unique_ptr MissingReleaseBugType; + std::unique_ptr ExtraReleaseBugType; + +public: + ObjCDeallocChecker(); + + void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr, + BugReporter &BR) const; + void checkBeginFunction(CheckerContext &Ctx) const; + void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; + void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; + + ProgramStateRef checkPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind) const; + void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const; + void checkEndFunction(CheckerContext &Ctx) const; + +private: + void diagnoseMissingReleases(CheckerContext &C) const; + + bool diagnoseExtraRelease(SymbolRef ReleasedValue, const ObjCMethodCall &M, + CheckerContext &C) const; + + SymbolRef getValueExplicitlyReleased(const ObjCMethodCall &M, + CheckerContext &C) const; + SymbolRef getValueReleasedByNillingOut(const ObjCMethodCall &M, + CheckerContext &C) const; + + SymbolRef getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym) const; + + ReleaseRequirement + getDeallocReleaseRequirement(const ObjCPropertyImplDecl *PropImpl) const; + + bool isInInstanceDealloc(const CheckerContext &C, SVal &SelfValOut) const; + bool isInInstanceDealloc(const CheckerContext &C, const LocationContext *LCtx, + SVal &SelfValOut) const; + bool instanceDeallocIsOnStack(const CheckerContext &C, + SVal &InstanceValOut) const; + + bool isSuperDeallocMessage(const ObjCMethodCall &M) const; + + const ObjCImplDecl *getContainingObjCImpl(const LocationContext *LCtx) const; + + const ObjCPropertyDecl * + findShadowedPropertyDecl(const ObjCPropertyImplDecl *PropImpl) const; + + ProgramStateRef removeValueRequiringRelease(ProgramStateRef State, + SymbolRef InstanceSym, + SymbolRef ValueSym) const; + + void initIdentifierInfoAndSelectors(ASTContext &Ctx) const; + + bool classHasSeparateTeardown(const ObjCInterfaceDecl *ID) const; +}; +} // End anonymous namespace. + +typedef llvm::ImmutableSet SymbolSet; + +/// Maps from the symbol for a class instance to the set of +/// symbols remaining that must be released in -dealloc. +REGISTER_MAP_WITH_PROGRAMSTATE(UnreleasedIvarMap, SymbolRef, SymbolSet); + +template<> struct ProgramStateTrait +: public ProgramStatePartialTrait { + static void *GDMIndex() { static int index = 0; return &index; } +}; -static void checkObjCDealloc(const CheckerBase *Checker, - const ObjCImplementationDecl *D, - const LangOptions &LOpts, BugReporter &BR) { - assert(LOpts.getGC() != LangOptions::GCOnly); - assert(!LOpts.ObjCAutoRefCount); +/// An AST check that diagnose when the class requires a -dealloc method and +/// is missing one. +void ObjCDeallocChecker::checkASTDecl(const ObjCImplementationDecl *D, + AnalysisManager &Mgr, + BugReporter &BR) const { + assert(Mgr.getLangOpts().getGC() != LangOptions::GCOnly); + assert(!Mgr.getLangOpts().ObjCAutoRefCount); + initIdentifierInfoAndSelectors(Mgr.getASTContext()); - ASTContext &Ctx = BR.getContext(); const ObjCInterfaceDecl *ID = D->getClassInterface(); // Does the class contain any synthesized properties that are retainable? // If not, skip the check entirely. bool containsRetainedSynthesizedProperty = false; for (const auto *I : D->property_impls()) { - const ObjCIvarDecl *ID = nullptr; - const ObjCPropertyDecl *PD = nullptr; - if (!isSynthesizedRetainableProperty(I, &ID, &PD)) - continue; - - if (synthesizedPropertyRequiresRelease(PD)) { + if (getDeallocReleaseRequirement(I) == ReleaseRequirement::MustRelease) { containsRetainedSynthesizedProperty = true; break; } @@ -142,136 +193,658 @@ static void checkObjCDealloc(const CheckerBase *Checker, if (!containsRetainedSynthesizedProperty) return; - // Determine if the class subclasses NSObject. - IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject"); - IdentifierInfo* SenTestCaseII = &Ctx.Idents.get("SenTestCase"); - - for ( ; ID ; ID = ID->getSuperClass()) { - IdentifierInfo *II = ID->getIdentifier(); - - if (II == NSObjectII) - break; - - // FIXME: For now, ignore classes that subclass SenTestCase, as these don't - // need to implement -dealloc. They implement tear down in another way, - // which we should try and catch later. - // http://llvm.org/bugs/show_bug.cgi?id=3187 - if (II == SenTestCaseII) - return; - } - - if (!ID) + // If the class is known to have a lifecycle with a separate teardown method + // then it may not require a -dealloc method. + if (classHasSeparateTeardown(ID)) return; - // Get the "dealloc" selector. - IdentifierInfo* II = &Ctx.Idents.get("dealloc"); - Selector S = Ctx.Selectors.getSelector(0, &II); const ObjCMethodDecl *MD = nullptr; // Scan the instance methods for "dealloc". for (const auto *I : D->instance_methods()) { - if (I->getSelector() == S) { + if (I->getSelector() == DeallocSel) { MD = I; break; } } if (!MD) { // No dealloc found. + const char* Name = "Missing -dealloc"; - const char* name = LOpts.getGC() == LangOptions::NonGC - ? "missing -dealloc" - : "missing -dealloc (Hybrid MM, non-GC)"; - - std::string buf; - llvm::raw_string_ostream os(buf); - os << "Objective-C class '" << *D << "' lacks a 'dealloc' instance method"; + std::string Buf; + llvm::raw_string_ostream OS(Buf); + OS << "Objective-C class '" << *D << "' lacks a 'dealloc' instance method"; PathDiagnosticLocation DLoc = PathDiagnosticLocation::createBegin(D, BR.getSourceManager()); - BR.EmitBasicReport(D, Checker, name, categories::CoreFoundationObjectiveC, - os.str(), DLoc); + BR.EmitBasicReport(D, this, Name, categories::CoreFoundationObjectiveC, + OS.str(), DLoc); return; } +} + +/// If this is the beginning of -dealloc, mark the values initially stored in +/// instance variables that must be released by the end of -dealloc +/// as unreleased in the state. +void ObjCDeallocChecker::checkBeginFunction( + CheckerContext &C) const { + initIdentifierInfoAndSelectors(C.getASTContext()); - // Get the "release" selector. - IdentifierInfo* RII = &Ctx.Idents.get("release"); - Selector RS = Ctx.Selectors.getSelector(0, &RII); + // Only do this if the current method is -dealloc. + SVal SelfVal; + if (!isInInstanceDealloc(C, SelfVal)) + return; - // Get the "self" identifier - IdentifierInfo* SelfII = &Ctx.Idents.get("self"); + SymbolRef SelfSymbol = SelfVal.getAsSymbol(); - // Scan for missing and extra releases of ivars used by implementations - // of synthesized properties - for (const auto *I : D->property_impls()) { - const ObjCIvarDecl *ID = nullptr; - const ObjCPropertyDecl *PD = nullptr; - if (!isSynthesizedRetainableProperty(I, &ID, &PD)) + const LocationContext *LCtx = C.getLocationContext(); + ProgramStateRef InitialState = C.getState(); + + ProgramStateRef State = InitialState; + + SymbolSet::Factory &F = State->getStateManager().get_context(); + + // Symbols that must be released by the end of the -dealloc; + SymbolSet RequiredReleases = F.getEmptySet(); + + // If we're an inlined -dealloc, we should add our symbols to the existing + // set from our subclass. + if (const SymbolSet *CurrSet = State->get(SelfSymbol)) + RequiredReleases = *CurrSet; + + for (auto *PropImpl : getContainingObjCImpl(LCtx)->property_impls()) { + ReleaseRequirement Requirement = getDeallocReleaseRequirement(PropImpl); + if (Requirement != ReleaseRequirement::MustRelease) continue; - bool requiresRelease = synthesizedPropertyRequiresRelease(PD); - if (scan_ivar_release(MD->getBody(), ID, PD, RS, SelfII, Ctx) - != requiresRelease) { - const char *name = nullptr; - std::string buf; - llvm::raw_string_ostream os(buf); - - if (requiresRelease) { - name = LOpts.getGC() == LangOptions::NonGC - ? "missing ivar release (leak)" - : "missing ivar release (Hybrid MM, non-GC)"; - - os << "The '" << *ID << "' instance variable in '" << *D - << "' was retained by a synthesized property " - "but was not released in 'dealloc'"; - } else { - // It is common for the ivars for read-only assign properties to - // always be stored retained, so don't warn for a release in - // dealloc for the ivar backing these properties. - if (PD->isReadOnly()) - continue; - - name = LOpts.getGC() == LangOptions::NonGC - ? "extra ivar release (use-after-release)" - : "extra ivar release (Hybrid MM, non-GC)"; - - os << "The '" << *ID << "' instance variable in '" << *D - << "' was not retained by a synthesized property " - "but was released in 'dealloc'"; - } - - // If @synthesize statement is missing, fall back to @property statement. - const Decl *SPDecl = I->getLocation().isValid() - ? static_cast(I) - : static_cast(PD); - PathDiagnosticLocation SPLoc = - PathDiagnosticLocation::createBegin(SPDecl, BR.getSourceManager()); - - BR.EmitBasicReport(MD, Checker, name, - categories::CoreFoundationObjectiveC, os.str(), SPLoc); - } + SVal LVal = State->getLValue(PropImpl->getPropertyIvarDecl(), SelfVal); + Optional LValLoc = LVal.getAs(); + if (!LValLoc) + continue; + + SVal InitialVal = State->getSVal(LValLoc.getValue()); + SymbolRef Symbol = InitialVal.getAsSymbol(); + if (!Symbol || !isa(Symbol)) + continue; + + // Mark the value as requiring a release. + RequiredReleases = F.add(RequiredReleases, Symbol); + } + + if (!RequiredReleases.isEmpty()) { + State = State->set(SelfSymbol, RequiredReleases); + } + + if (State != InitialState) { + C.addTransition(State); } } -//===----------------------------------------------------------------------===// -// ObjCDeallocChecker -//===----------------------------------------------------------------------===// +/// Given a symbol for an ivar, return a symbol for the instance containing +/// the ivar. Returns nullptr if the instance symbol cannot be found. +SymbolRef +ObjCDeallocChecker::getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym) const { + if (auto *SRV = dyn_cast(IvarSym)) { + const TypedValueRegion *TVR = SRV->getRegion(); + const ObjCIvarRegion *IvarRegion = dyn_cast_or_null(TVR); + if (!IvarRegion) + return nullptr; + + return IvarRegion->getSymbolicBase()->getSymbol(); + } -namespace { -class ObjCDeallocChecker : public Checker< - check::ASTDecl > { -public: - void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr, - BugReporter &BR) const { - if (mgr.getLangOpts().getGC() == LangOptions::GCOnly || - mgr.getLangOpts().ObjCAutoRefCount) + return nullptr; +} + +/// If we are in -dealloc or -dealloc is on the stack, handle the call if it is +/// a release or a nilling-out property setter. +void ObjCDeallocChecker::checkPreObjCMessage( + const ObjCMethodCall &M, CheckerContext &C) const { + // Only run if -dealloc is on the stack. + SVal DeallocedInstance; + if (!instanceDeallocIsOnStack(C, DeallocedInstance)) + return; + + SymbolRef ReleasedValue = getValueExplicitlyReleased(M, C); + + if (ReleasedValue) { + // An instance variable symbol was released with -release: + // [_property release]; + if (diagnoseExtraRelease(ReleasedValue,M, C)) return; - checkObjCDealloc(this, cast(D), mgr.getLangOpts(), - BR); + } else { + // An instance variable symbol was released nilling out its property: + // self.property = nil; + ReleasedValue = getValueReleasedByNillingOut(M, C); } -}; + + if (!ReleasedValue) + return; + + SymbolRef InstanceSym = getInstanceSymbolFromIvarSymbol(ReleasedValue); + if (!InstanceSym) + return; + ProgramStateRef InitialState = C.getState(); + + ProgramStateRef ReleasedState = + removeValueRequiringRelease(InitialState, InstanceSym, ReleasedValue); + + if (ReleasedState != InitialState) { + C.addTransition(ReleasedState); + } +} + +/// If the message was a call to '[super dealloc]', diagnose any missing +/// releases. +void ObjCDeallocChecker::checkPostObjCMessage( + const ObjCMethodCall &M, CheckerContext &C) const { + // We perform this check post-message so that if the super -dealloc + // calls a helper method and that this class overrides, any ivars released in + // the helper method will be recorded before checking. + if (isSuperDeallocMessage(M)) + diagnoseMissingReleases(C); +} + +/// Check for missing releases even when -dealloc does not call +/// '[super dealloc]'. +void ObjCDeallocChecker::checkEndFunction( + CheckerContext &C) const { + diagnoseMissingReleases(C); +} + +/// Check for missing releases on early return. +void ObjCDeallocChecker::checkPreStmt( + const ReturnStmt *RS, CheckerContext &C) const { + diagnoseMissingReleases(C); } -void ento::registerObjCDeallocChecker(CheckerManager &mgr) { - mgr.registerChecker(); +/// If a symbol escapes conservatively assume unseen code released it. +ProgramStateRef ObjCDeallocChecker::checkPointerEscape( + ProgramStateRef State, const InvalidatedSymbols &Escaped, + const CallEvent *Call, PointerEscapeKind Kind) const { + + // Don't treat calls to '[super dealloc]' as escaping for the purposes + // of this checker. Because the checker diagnoses missing releases in the + // post-message handler for '[super dealloc], escaping here would cause + // the checker to never warn. + auto *OMC = dyn_cast_or_null(Call); + if (OMC && isSuperDeallocMessage(*OMC)) + return State; + + for (const auto &Sym : Escaped) { + State = State->remove(Sym); + + SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(Sym); + if (!InstanceSymbol) + continue; + + State = removeValueRequiringRelease(State, InstanceSymbol, Sym); + } + + return State; +} + +/// Report any unreleased instance variables for the current instance being +/// dealloced. +void ObjCDeallocChecker::diagnoseMissingReleases(CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + SVal SelfVal; + if (!isInInstanceDealloc(C, SelfVal)) + return; + + const MemRegion *SelfRegion = SelfVal.castAs().getRegion(); + const LocationContext *LCtx = C.getLocationContext(); + + ExplodedNode *ErrNode = nullptr; + + SymbolRef SelfSym = SelfVal.getAsSymbol(); + if (!SelfSym) + return; + + const SymbolSet *OldUnreleased = State->get(SelfSym); + if (!OldUnreleased) + return; + + SymbolSet NewUnreleased = *OldUnreleased; + SymbolSet::Factory &F = State->getStateManager().get_context(); + + ProgramStateRef InitialState = State; + + for (auto *IvarSymbol : *OldUnreleased) { + const TypedValueRegion *TVR = + cast(IvarSymbol)->getRegion(); + const ObjCIvarRegion *IvarRegion = cast(TVR); + + // Don't warn if the ivar is not for this instance. + if (SelfRegion != IvarRegion->getSuperRegion()) + continue; + + // Prevent an inlined call to -dealloc in a super class from warning + // about the values the subclass's -dealloc should release. + if (IvarRegion->getDecl()->getContainingInterface() != + cast(LCtx->getDecl())->getClassInterface()) + continue; + + // Prevents diagnosing multiple times for the same instance variable + // at, for example, both a return and at the end of of the function. + NewUnreleased = F.remove(NewUnreleased, IvarSymbol); + + if (State->getStateManager() + .getConstraintManager() + .isNull(State, IvarSymbol) + .isConstrainedTrue()) { + continue; + } + + // A missing release manifests as a leak, so treat as a non-fatal error. + if (!ErrNode) + ErrNode = C.generateNonFatalErrorNode(); + // If we've already reached this node on another path, return without + // diagnosing. + if (!ErrNode) + return; + + std::string Buf; + llvm::raw_string_ostream OS(Buf); + + const ObjCIvarDecl *IvarDecl = IvarRegion->getDecl(); + const ObjCInterfaceDecl *Interface = IvarDecl->getContainingInterface(); + // If the class is known to have a lifecycle with teardown that is + // separate from -dealloc, do not warn about missing releases. We + // suppress here (rather than not tracking for instance variables in + // such classes) because these classes are rare. + if (classHasSeparateTeardown(Interface)) + return; + + ObjCImplDecl *ImplDecl = Interface->getImplementation(); + + const ObjCPropertyImplDecl *PropImpl = + ImplDecl->FindPropertyImplIvarDecl(IvarDecl->getIdentifier()); + + const ObjCPropertyDecl *PropDecl = PropImpl->getPropertyDecl(); + + assert(PropDecl->getSetterKind() == ObjCPropertyDecl::Copy || + PropDecl->getSetterKind() == ObjCPropertyDecl::Retain); + + OS << "The '" << *IvarDecl << "' ivar in '" << *ImplDecl + << "' was "; + + if (PropDecl->getSetterKind() == ObjCPropertyDecl::Retain) + OS << "retained"; + else + OS << "copied"; + + OS << " by a synthesized property but not released" + " before '[super dealloc]'"; + + std::unique_ptr BR( + new BugReport(*MissingReleaseBugType, OS.str(), ErrNode)); + + C.emitReport(std::move(BR)); + } + + if (NewUnreleased.isEmpty()) { + State = State->remove(SelfSym); + } else { + State = State->set(SelfSym, NewUnreleased); + } + + if (ErrNode) { + C.addTransition(State, ErrNode); + } else if (State != InitialState) { + C.addTransition(State); + } + + // Make sure that after checking in the top-most frame the list of + // tracked ivars is empty. This is intended to detect accidental leaks in + // the UnreleasedIvarMap program state. + assert(!LCtx->inTopFrame() || State->get().isEmpty()); +} + +/// Emits a warning if the current context is -dealloc and \param ReleasedValue +/// must not be directly released in a -dealloc. Returns true if a diagnostic +/// was emitted. +bool ObjCDeallocChecker::diagnoseExtraRelease(SymbolRef ReleasedValue, + const ObjCMethodCall &M, + CheckerContext &C) const { + SVal DeallocedInstance; + if (!isInInstanceDealloc(C, DeallocedInstance)) + return false; + + // Try to get the region from which the the released value was loaded. + // Note that, unlike diagnosing for missing releases, here we don't track + // values that must not be released in the state. This is because even if + // these values escape, it is still an error under the rules of MRR to + // release them in -dealloc. + const MemRegion *RegionLoadedFrom = nullptr; + if (auto *DerivedSym = dyn_cast(ReleasedValue)) + RegionLoadedFrom = DerivedSym->getRegion(); + else if (auto *RegionSym = dyn_cast(ReleasedValue)) + RegionLoadedFrom = RegionSym->getRegion(); + else + return false; + + auto *ReleasedIvar = dyn_cast(RegionLoadedFrom); + if (!ReleasedIvar) + return false; + + if (DeallocedInstance.castAs().getRegion() != + ReleasedIvar->getSuperRegion()) + return false; + + const LocationContext *LCtx = C.getLocationContext(); + const ObjCIvarDecl *ReleasedIvarDecl = ReleasedIvar->getDecl(); + + // If the ivar belongs to a property that must not be released directly + // in dealloc, emit a warning. + const ObjCImplDecl *Container = getContainingObjCImpl(LCtx); + const ObjCPropertyImplDecl *PropImpl = + Container->FindPropertyImplIvarDecl(ReleasedIvarDecl->getIdentifier()); + if (!PropImpl) + return false; + + if (getDeallocReleaseRequirement(PropImpl) != + ReleaseRequirement::MustNotReleaseDirectly) { + return false; + } + + // If the property is readwrite but it shadows a read-only property in its + // external interface, treat the property a read-only. If the outside + // world cannot write to a property then the internal implementation is free + // to make its own convention about whether the value is stored retained + // or not. We look up the shadow here rather than in + // getDeallocReleaseRequirement() because doing so can be expensive. + const ObjCPropertyDecl *PropDecl = findShadowedPropertyDecl(PropImpl); + if (PropDecl) { + if (PropDecl->isReadOnly()) + return false; + } else { + PropDecl = PropImpl->getPropertyDecl(); + } + + ExplodedNode *ErrNode = C.generateNonFatalErrorNode(); + if (!ErrNode) + return false; + + std::string Buf; + llvm::raw_string_ostream OS(Buf); + + assert(PropDecl->getSetterKind() == ObjCPropertyDecl::Weak || + (PropDecl->getSetterKind() == ObjCPropertyDecl::Assign && + !PropDecl->isReadOnly())); + + OS << "The '" << *PropImpl->getPropertyIvarDecl() + << "' ivar in '" << *Container + << "' was synthesized for "; + + if (PropDecl->getSetterKind() == ObjCPropertyDecl::Weak) + OS << "a weak"; + else + OS << "an assign, readwrite"; + + OS << " property but was released in 'dealloc'"; + + std::unique_ptr BR( + new BugReport(*ExtraReleaseBugType, OS.str(), ErrNode)); + BR->addRange(M.getOriginExpr()->getSourceRange()); + + C.emitReport(std::move(BR)); + + return true; +} + + +ObjCDeallocChecker:: + ObjCDeallocChecker() + : NSObjectII(nullptr), SenTestCaseII(nullptr) { + + MissingReleaseBugType.reset( + new BugType(this, "Missing ivar release (leak)", + categories::MemoryCoreFoundationObjectiveC)); + + ExtraReleaseBugType.reset( + new BugType(this, "Extra ivar release", + categories::MemoryCoreFoundationObjectiveC)); +} + +void ObjCDeallocChecker::initIdentifierInfoAndSelectors( + ASTContext &Ctx) const { + if (NSObjectII) + return; + + NSObjectII = &Ctx.Idents.get("NSObject"); + SenTestCaseII = &Ctx.Idents.get("SenTestCase"); + + IdentifierInfo *DeallocII = &Ctx.Idents.get("dealloc"); + IdentifierInfo *ReleaseII = &Ctx.Idents.get("release"); + DeallocSel = Ctx.Selectors.getSelector(0, &DeallocII); + ReleaseSel = Ctx.Selectors.getSelector(0, &ReleaseII); +} + +/// Returns true if \param M is a call to '[super dealloc]'. +bool ObjCDeallocChecker::isSuperDeallocMessage( + const ObjCMethodCall &M) const { + if (M.getOriginExpr()->getReceiverKind() != ObjCMessageExpr::SuperInstance) + return false; + + return M.getSelector() == DeallocSel; +} + +/// Returns the ObjCImplDecl containing the method declaration in \param LCtx. +const ObjCImplDecl * +ObjCDeallocChecker::getContainingObjCImpl(const LocationContext *LCtx) const { + auto *MD = cast(LCtx->getDecl()); + return cast(MD->getDeclContext()); +} + +/// Returns the property that shadowed by \param PropImpl if one exists and +/// nullptr otherwise. +const ObjCPropertyDecl *ObjCDeallocChecker::findShadowedPropertyDecl( + const ObjCPropertyImplDecl *PropImpl) const { + const ObjCPropertyDecl *PropDecl = PropImpl->getPropertyDecl(); + + // Only readwrite properties can shadow. + if (PropDecl->isReadOnly()) + return nullptr; + + auto *CatDecl = dyn_cast(PropDecl->getDeclContext()); + + // Only class extensions can contain shadowing properties. + if (!CatDecl || !CatDecl->IsClassExtension()) + return nullptr; + + IdentifierInfo *ID = PropDecl->getIdentifier(); + DeclContext::lookup_result R = CatDecl->getClassInterface()->lookup(ID); + for (DeclContext::lookup_iterator I = R.begin(), E = R.end(); I != E; ++I) { + auto *ShadowedPropDecl = dyn_cast(*I); + if (!ShadowedPropDecl) + continue; + + if (ShadowedPropDecl->isInstanceProperty()) { + assert(ShadowedPropDecl->isReadOnly()); + return ShadowedPropDecl; + } + } + + return nullptr; +} + +/// Remove the \param Value requiring a release from the tracked set for +/// \param Instance and return the resultant state. +ProgramStateRef ObjCDeallocChecker::removeValueRequiringRelease( + ProgramStateRef State, SymbolRef Instance, SymbolRef Value) const { + assert(Instance); + assert(Value); + + const SymbolSet *Unreleased = State->get(Instance); + if (!Unreleased) + return State; + + // Mark the value as no longer requiring a release. + SymbolSet::Factory &F = State->getStateManager().get_context(); + SymbolSet NewUnreleased = F.remove(*Unreleased, Value); + + if (NewUnreleased.isEmpty()) { + return State->remove(Instance); + } + + return State->set(Instance, NewUnreleased); +} + +/// Determines whether the instance variable for \p PropImpl must or must not be +/// released in -dealloc or whether it cannot be determined. +ReleaseRequirement ObjCDeallocChecker::getDeallocReleaseRequirement( + const ObjCPropertyImplDecl *PropImpl) const { + const ObjCIvarDecl *IvarDecl; + const ObjCPropertyDecl *PropDecl; + if (!isSynthesizedRetainableProperty(PropImpl, &IvarDecl, &PropDecl)) + return ReleaseRequirement::Unknown; + + ObjCPropertyDecl::SetterKind SK = PropDecl->getSetterKind(); + + switch (SK) { + // Retain and copy setters retain/copy their values before storing and so + // the value in their instance variables must be released in -dealloc. + case ObjCPropertyDecl::Retain: + case ObjCPropertyDecl::Copy: + return ReleaseRequirement::MustRelease; + + case ObjCPropertyDecl::Weak: + return ReleaseRequirement::MustNotReleaseDirectly; + + case ObjCPropertyDecl::Assign: + // It is common for the ivars for read-only assign properties to + // always be stored retained, so their release requirement cannot be + // be determined. + if (PropDecl->isReadOnly()) + return ReleaseRequirement::Unknown; + + return ReleaseRequirement::MustNotReleaseDirectly; + } +} + +/// Returns the released value if \param M is a call to -release. Returns +/// nullptr otherwise. +SymbolRef +ObjCDeallocChecker::getValueExplicitlyReleased(const ObjCMethodCall &M, + CheckerContext &C) const { + if (M.getSelector() != ReleaseSel) + return nullptr; + + return M.getReceiverSVal().getAsSymbol(); +} + +/// Returns the released value if \param M is a call a setter that releases +/// and nils out its underlying instance variable. +SymbolRef +ObjCDeallocChecker::getValueReleasedByNillingOut(const ObjCMethodCall &M, + CheckerContext &C) const { + SVal ReceiverVal = M.getReceiverSVal(); + if (!ReceiverVal.isValid()) + return nullptr; + + // Is the first argument nil? + if (M.getNumArgs() == 0) + return nullptr; + SVal Arg = M.getArgSVal(0); + ProgramStateRef notNilState, nilState; + std::tie(notNilState, nilState) = + M.getState()->assume(Arg.castAs()); + if (!(nilState && !notNilState)) + return nullptr; + + const ObjCPropertyDecl *Prop = M.getAccessedProperty(); + if (!Prop) + return nullptr; + + ObjCIvarDecl *PropIvarDecl = Prop->getPropertyIvarDecl(); + if (!PropIvarDecl) + return nullptr; + + ProgramStateRef State = C.getState(); + + SVal LVal = State->getLValue(PropIvarDecl, ReceiverVal); + Optional LValLoc = LVal.getAs(); + if (!LValLoc) + return nullptr; + + SVal CurrentValInIvar = State->getSVal(LValLoc.getValue()); + return CurrentValInIvar.getAsSymbol(); +} + +/// Returns true if the current context is a call to -dealloc and false +/// otherwise. If true, it also sets \param SelfValOut to the value of +/// 'self'. +bool ObjCDeallocChecker::isInInstanceDealloc(const CheckerContext &C, + SVal &SelfValOut) const { + return isInInstanceDealloc(C, C.getLocationContext(), SelfValOut); +} + +/// Returns true if \param LCtx is a call to -dealloc and false +/// otherwise. If true, it also sets \param SelfValOut to the value of +/// 'self'. +bool ObjCDeallocChecker::isInInstanceDealloc(const CheckerContext &C, + const LocationContext *LCtx, + SVal &SelfValOut) const { + auto *MD = dyn_cast(LCtx->getDecl()); + if (!MD || !MD->isInstanceMethod() || MD->getSelector() != DeallocSel) + return false; + + const ImplicitParamDecl *SelfDecl = LCtx->getSelfDecl(); + assert(SelfDecl && "No self in -dealloc?"); + + ProgramStateRef State = C.getState(); + SelfValOut = State->getSVal(State->getRegion(SelfDecl, LCtx)); + return true; +} + +/// Returns true if there is a call to -dealloc anywhere on the stack and false +/// otherwise. If true, it also sets \param InstanceValOut to the value of +/// 'self' in the frame for -dealloc. +bool ObjCDeallocChecker::instanceDeallocIsOnStack(const CheckerContext &C, + SVal &InstanceValOut) const { + const LocationContext *LCtx = C.getLocationContext(); + + while (LCtx) { + if (isInInstanceDealloc(C, LCtx, InstanceValOut)) + return true; + + LCtx = LCtx->getParent(); + } + + return false; +} + +/// Returns true if the \param ID is a class in which which is known to have +/// a separate teardown lifecycle. In this case, -dealloc warnings +/// about missing releases should be suppressed. +bool ObjCDeallocChecker::classHasSeparateTeardown( + const ObjCInterfaceDecl *ID) const { + // Suppress if the class is not a subclass of NSObject. + for ( ; ID ; ID = ID->getSuperClass()) { + IdentifierInfo *II = ID->getIdentifier(); + + if (II == NSObjectII) + return false; + + // FIXME: For now, ignore classes that subclass SenTestCase, as these don't + // need to implement -dealloc. They implement tear down in another way, + // which we should try and catch later. + // http://llvm.org/bugs/show_bug.cgi?id=3187 + if (II == SenTestCaseII) + return true; + } + + return true; +} + +void ento::registerObjCDeallocChecker(CheckerManager &Mgr) { + const LangOptions &LangOpts = Mgr.getLangOpts(); + // These checker only makes sense under MRR. + if (LangOpts.getGC() == LangOptions::GCOnly || LangOpts.ObjCAutoRefCount) + return; + + Mgr.registerChecker(); } diff --git a/test/Analysis/DeallocMissingRelease.m b/test/Analysis/DeallocMissingRelease.m index e782f9968f9..c7769db3d5f 100644 --- a/test/Analysis/DeallocMissingRelease.m +++ b/test/Analysis/DeallocMissingRelease.m @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.osx.cocoa.Dealloc -fblocks %s 2>&1 | FileCheck -check-prefix=CHECK %s -// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.osx.cocoa.Dealloc -fblocks -triple x86_64-apple-darwin10 -fobjc-arc -fobjc-runtime-has-weak %s 2>&1 | FileCheck -check-prefix=CHECK-ARC -allow-empty '--implicit-check-not=error:' '--implicit-check-not=warning:' %s +// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.osx.cocoa.Dealloc -fblocks -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.osx.cocoa.Dealloc -fblocks -triple x86_64-apple-darwin10 -fobjc-arc -fobjc-runtime-has-weak -verify %s #define nil ((id)0) @@ -11,6 +11,11 @@ #define WEAK_ON_ARC __weak #endif +// No diagnostics expected under ARC. +#if !NON_ARC + // expected-no-diagnostics +#endif + typedef signed char BOOL; @protocol NSObject - (BOOL)isEqual:(id)object; @@ -18,6 +23,7 @@ - (Class)class; @end @interface NSObject {} ++ (instancetype)alloc; - (void)dealloc; - (id)init; - (id)retain; @@ -26,7 +32,6 @@ - (oneway void)release; typedef struct objc_selector *SEL; -//===------------------------------------------------------------------------=== // Do not warn about missing release in -dealloc for ivars. @interface MyIvarClass1 : NSObject { @@ -87,11 +92,9 @@ - (void)setIvar:(NSObject *)ivar } @end -//===------------------------------------------------------------------------=== // Warn about missing release in -dealloc for properties. @interface MyPropertyClass1 : NSObject -// CHECK: DeallocMissingRelease.m:[[@LINE+1]]:1: warning: The '_ivar' instance variable in 'MyPropertyClass1' was retained by a synthesized property but was not released in 'dealloc' @property (copy) NSObject *ivar; @end @@ -99,13 +102,12 @@ @implementation MyPropertyClass1 - (void)dealloc { #if NON_ARC - [super dealloc]; + [super dealloc]; // expected-warning {{The '_ivar' ivar in 'MyPropertyClass1' was copied by a synthesized property but not released before '[super dealloc]'}} #endif } @end @interface MyPropertyClass2 : NSObject -// CHECK: DeallocMissingRelease.m:[[@LINE+1]]:1: warning: The '_ivar' instance variable in 'MyPropertyClass2' was retained by a synthesized property but was not released in 'dealloc' @property (retain) NSObject *ivar; @end @@ -113,7 +115,7 @@ @implementation MyPropertyClass2 - (void)dealloc { #if NON_ARC - [super dealloc]; + [super dealloc]; // expected-warning {{The '_ivar' ivar in 'MyPropertyClass2' was retained by a synthesized property but not released before '[super dealloc]'}} #endif } @end @@ -125,14 +127,14 @@ @interface MyPropertyClass3 : NSObject { @end @implementation MyPropertyClass3 -// CHECK: DeallocMissingRelease.m:[[@LINE+1]]:1: warning: The '_ivar' instance variable in 'MyPropertyClass3' was retained by a synthesized property but was not released in 'dealloc' @synthesize ivar = _ivar; - (void)dealloc { #if NON_ARC - [super dealloc]; + [super dealloc]; // expected-warning {{The '_ivar' ivar in 'MyPropertyClass3' was retained by a synthesized property but not released before '[super dealloc]'}} #endif } + @end @interface MyPropertyClass4 : NSObject { @@ -142,12 +144,11 @@ @interface MyPropertyClass4 : NSObject { @end @implementation MyPropertyClass4 -// CHECK: DeallocMissingRelease.m:[[@LINE+1]]:1: warning: The '_blockPropertyIvar' instance variable in 'MyPropertyClass4' was retained by a synthesized property but was not released in 'dealloc' @synthesize blockProperty = _blockPropertyIvar; - (void)dealloc { #if NON_ARC - [super dealloc]; + [super dealloc]; // expected-warning {{The '_blockPropertyIvar' ivar in 'MyPropertyClass4' was copied by a synthesized property but not released before '[super dealloc]'}} #endif } @end @@ -159,16 +160,77 @@ @interface MyPropertyClass5 : NSObject { @end @implementation MyPropertyClass5 -@synthesize ivar = _ivar; // no-warning +@synthesize ivar = _ivar; - (void)dealloc { #if NON_ARC + [super dealloc]; // no-warning because it is a weak property +#endif +} +@end + +@interface MyPropertyClassWithReturnInDealloc : NSObject { + NSObject *_ivar; +} +@property (retain) NSObject *ivar; +@end + +@implementation MyPropertyClassWithReturnInDealloc +@synthesize ivar = _ivar; +- (void)dealloc +{ + return; +#if NON_ARC + // expected-warning@-2{{The '_ivar' ivar in 'MyPropertyClassWithReturnInDealloc' was retained by a synthesized property but not released before '[super dealloc]'}} [super dealloc]; #endif } @end -//===------------------------------------------------------------------------=== +@interface MyPropertyClassWithReleaseInOtherInstance : NSObject { + NSObject *_ivar; + MyPropertyClassWithReleaseInOtherInstance *_other; +} +@property (retain) NSObject *ivar; + +-(void)releaseIvars; +@end + +@implementation MyPropertyClassWithReleaseInOtherInstance +@synthesize ivar = _ivar; + +-(void)releaseIvars; { +#if NON_ARC + [_ivar release]; +#endif +} + +- (void)dealloc +{ + [_other releaseIvars]; +#if NON_ARC + [super dealloc]; // expected-warning {{The '_ivar' ivar in 'MyPropertyClassWithReleaseInOtherInstance' was retained by a synthesized property but not released before '[super dealloc]'}} +#endif +} +@end + +@interface MyPropertyClassWithNeitherReturnNorSuperDealloc : NSObject { + NSObject *_ivar; +} +@property (retain) NSObject *ivar; +@end + +@implementation MyPropertyClassWithNeitherReturnNorSuperDealloc +@synthesize ivar = _ivar; +- (void)dealloc +{ +} +#if NON_ARC + // expected-warning@-2 {{method possibly missing a [super dealloc] call}} (From Sema) + // expected-warning@-3{{The '_ivar' ivar in 'MyPropertyClassWithNeitherReturnNorSuperDealloc' was retained by a synthesized property but not released before '[super dealloc]'}} +#endif +@end + // : 'myproperty' has kind 'assign' and thus the // assignment through the setter does not perform a release. @@ -202,51 +264,419 @@ - (void)dealloc; { // We really should warn because there is a path through -dealloc on which // _ivar2 is not released. #if NON_ARC - [_ivar2 release]; // no-warning + [_ivar2 release]; #endif } #if NON_ARC - [super dealloc]; + [super dealloc]; // expected-warning {{The '_ivar2' ivar in 'ClassWithControlFlowInRelease' was retained by a synthesized property but not released before '[super dealloc]'}} #endif } - @end -//===------------------------------------------------------------------------=== // Don't warn when the property is nil'd out in -dealloc @interface ClassWithNildOutProperty : NSObject -@property (retain) NSObject *ivar; // no-warning +@property (retain) NSObject *ivar; +@property (assign) int *intPtrProp; @end @implementation ClassWithNildOutProperty - (void)dealloc; { self.ivar = nil; + // Make sure to handle setting a non-retainable property to 0. + self.intPtrProp = 0; #if NON_ARC - [super dealloc]; + [super dealloc]; // no-warning +#endif +} +@end + +// Do warn when the ivar but not the property is nil'd out in -dealloc + +@interface ClassWithNildOutIvar : NSObject +@property (retain) NSObject *ivar; +@end + +@implementation ClassWithNildOutIvar +- (void)dealloc; { + // Oops. Meant self.ivar = nil + _ivar = nil; + +#if NON_ARC + [super dealloc]; // expected-warning {{The '_ivar' ivar in 'ClassWithNildOutIvar' was retained by a synthesized property but not released before '[super dealloc]'}} #endif } +@end +// Do warn when the ivar is updated to a different value that is then +// released. + +@interface ClassWithUpdatedIvar : NSObject +@property (retain) NSObject *ivar; @end -//===------------------------------------------------------------------------=== +@implementation ClassWithUpdatedIvar +- (void)dealloc; { + _ivar = [[NSObject alloc] init]; + +#if NON_ARC + [_ivar release]; +#endif + +#if NON_ARC + [super dealloc]; // expected-warning {{The '_ivar' ivar in 'ClassWithUpdatedIvar' was retained by a synthesized property but not released before '[super dealloc]'}} +#endif +} +@end + + // Don't warn when the property is nil'd out with a setter in -dealloc @interface ClassWithNildOutPropertyViaSetter : NSObject -@property (retain) NSObject *ivar; // no-warning +@property (retain) NSObject *ivar; @end @implementation ClassWithNildOutPropertyViaSetter - (void)dealloc; { [self setIvar:nil]; +#if NON_ARC + [super dealloc]; // no-warning +#endif +} +@end + + +// Don't warn about missing releases when -dealloc helpers are called. + +@interface ClassWithDeallocHelpers : NSObject +@property (retain) NSObject *ivarReleasedInMethod; +@property (retain) NSObject *propNilledOutInMethod; + +@property (retain) NSObject *ivarReleasedInFunction; +@property (retain) NSObject *propNilledOutInFunction; + +@property (retain) NSObject *ivarNeverReleased; +- (void)invalidateInMethod; +@end + +void ReleaseValueHelper(NSObject *iv) { +#if NON_ARC + [iv release]; +#endif +} + +void NilOutPropertyHelper(ClassWithDeallocHelpers *o) { + o.propNilledOutInFunction = nil; +} + +@implementation ClassWithDeallocHelpers +- (void)invalidateInMethod { +#if NON_ARC + [_ivarReleasedInMethod release]; +#endif + self.propNilledOutInMethod = nil; +} + +- (void)dealloc; { + ReleaseValueHelper(_ivarReleasedInFunction); + NilOutPropertyHelper(self); + + [self invalidateInMethod]; +#if NON_ARC + [super dealloc]; // expected-warning {{The '_ivarNeverReleased' ivar in 'ClassWithDeallocHelpers' was retained by a synthesized property but not released before '[super dealloc]'}} +#endif +} +@end + + +// Don't warn when self in -dealloc escapes. + +@interface ClassWhereSelfEscapesViaMethodCall : NSObject +@property (retain) NSObject *ivar; // no-warning +@end + +@interface ClassWhereSelfEscapesViaMethodCall (Other) +- (void)invalidate; // In other translation unit. +@end + +@implementation ClassWhereSelfEscapesViaMethodCall +- (void)dealloc; { + [self invalidate]; +#if NON_ARC + [super dealloc]; +#endif +} // no-warning +@end + +@interface ClassWhereSelfEscapesViaPropertyAccess : NSObject +@property (retain) NSObject *ivar; +@end + +@interface ClassWhereSelfEscapesViaPropertyAccess (Other) +// The implementation of this property is unknown and therefore could +// release ivar. +@property (retain) NSObject *otherIvar; +@end + +@implementation ClassWhereSelfEscapesViaPropertyAccess +- (void)dealloc; { + self.otherIvar = nil; #if NON_ARC [super dealloc]; #endif +} // no-warning +@end + +// Don't treat self as escaping when setter called on *synthesized* +// property. + +@interface ClassWhereSelfEscapesViaSynthesizedPropertyAccess : NSObject +@property (retain) NSObject *ivar; +@property (retain) NSObject *otherIvar; +@end + +@implementation ClassWhereSelfEscapesViaSynthesizedPropertyAccess +- (void)dealloc; { + self.otherIvar = nil; +#if NON_ARC + [super dealloc]; // expected-warning {{The '_ivar' ivar in 'ClassWhereSelfEscapesViaSynthesizedPropertyAccess' was retained by a synthesized property but not released before '[super dealloc]'}} +#endif } +@end + +// Don't warn when value escapes. +@interface ClassWhereIvarValueEscapes : NSObject +@property (retain) NSObject *ivar; +@end + +void ReleaseMe(id arg); + +@implementation ClassWhereIvarValueEscapes +- (void)dealloc; { + + ReleaseMe(_ivar); + +#if NON_ARC + [super dealloc]; +#endif +} // no-warning +@end + +// Don't warn when value is known to be nil. + +@interface ClassWhereIvarIsNil : NSObject +@property (retain) NSObject *ivarIsNil; +@end + +@implementation ClassWhereIvarIsNil +- (void)dealloc; { + +#if NON_ARC + if (_ivarIsNil) + [_ivarIsNil release]; + + [super dealloc]; +#endif +} // no-warning +@end + + +// Don't warn for non-retainable properties. + +@interface ClassWithNonRetainableProperty : NSObject +@property (assign) int *ivar; // no-warning +@end + +@implementation ClassWithNonRetainableProperty +- (void)dealloc; { +#if NON_ARC + [super dealloc]; +#endif +} // no-warning +@end + + +@interface SuperClassOfClassWithInlinedSuperDealloc : NSObject +@property (retain) NSObject *propInSuper; +@end + +@implementation SuperClassOfClassWithInlinedSuperDealloc +- (void)dealloc { +#if NON_ARC + [super dealloc]; // expected-warning {{The '_propInSuper' ivar in 'SuperClassOfClassWithInlinedSuperDealloc' was retained by a synthesized property but not released before '[super dealloc]'}} +#endif +} +@end + +@interface ClassWithInlinedSuperDealloc : SuperClassOfClassWithInlinedSuperDealloc +@property (retain) NSObject *propInSub; +@end + +@implementation ClassWithInlinedSuperDealloc +- (void)dealloc { +#if NON_ARC + [super dealloc]; // expected-warning {{The '_propInSub' ivar in 'ClassWithInlinedSuperDealloc' was retained by a synthesized property but not released before '[super dealloc]'}} +#endif +} +@end + + +@interface SuperClassOfClassWithInlinedSuperDeallocAndInvalidation : NSObject +@property (retain) NSObject *propInSuper; + +- (void)invalidate; +@end + +@implementation SuperClassOfClassWithInlinedSuperDeallocAndInvalidation + +- (void)invalidate { +#if NON_ARC + [_propInSuper release]; +#endif + _propInSuper = nil; +} + +- (void)dealloc { + [self invalidate]; +#if NON_ARC + [super dealloc]; // no-warning +#endif +} +@end + +@interface ClassWithInlinedSuperDeallocAndInvalidation : SuperClassOfClassWithInlinedSuperDeallocAndInvalidation +@property (retain) NSObject *propInSub; +@end + +@implementation ClassWithInlinedSuperDeallocAndInvalidation + +- (void)invalidate { +#if NON_ARC + [_propInSub release]; +#endif + [super invalidate]; +} + +- (void)dealloc { +#if NON_ARC + [super dealloc]; // no-warning +#endif +} +@end + + +@interface SuperClassOfClassThatEscapesBeforeInliningSuper : NSObject +@property (retain) NSObject *propInSuper; +@end + +@implementation SuperClassOfClassThatEscapesBeforeInliningSuper + +- (void)dealloc { + +#if NON_ARC + [super dealloc]; // expected-warning {{The '_propInSuper' ivar in 'SuperClassOfClassThatEscapesBeforeInliningSuper' was retained by a synthesized property but not released before '[super dealloc]'}} +#endif +} +@end + +@interface ClassThatEscapesBeforeInliningSuper : SuperClassOfClassThatEscapesBeforeInliningSuper +@property (retain) NSObject *propInSub; +@end + +@interface ClassThatEscapesBeforeInliningSuper (Other) +- (void)invalidate; // No implementation in translation unit. +@end + +@implementation ClassThatEscapesBeforeInliningSuper +- (void)dealloc { + [self invalidate]; + +#if NON_ARC + [super dealloc]; // no-warning +#endif +} +@end + + +#if NON_ARC +@interface ReleaseIvarInField : NSObject { + int _tag; + union { + NSObject *field1; + NSObject *field2; + } _someUnion; + + struct { + NSObject *field1; + } _someStruct; +} +@end + +@implementation ReleaseIvarInField +- (void)dealloc { + if (_tag) { + [_someUnion.field1 release]; + } else { + [_someUnion.field2 release]; + } + + [_someStruct.field1 release]; + [super dealloc]; +} +@end +#endif + +#if NON_ARC +@interface ReleaseIvarInArray : NSObject { + NSObject *_array[3]; +} +@end + +@implementation ReleaseIvarInArray +- (void)dealloc { + for (int i = 0; i < 3; i++) { + [_array[i] release]; + } + [super dealloc]; +} +@end +#endif + +// Don't warn about missing releases for subclasses of SenTestCase or +// for classes that are not subclasses of NSObject. + +@interface SenTestCase : NSObject {} +@end + +@interface MyClassTest : SenTestCase +@property (retain) NSObject *ivar; +@end + +@implementation MyClassTest +-(void)tearDown { +#if NON_ARC + [_ivar release]; +#endif +} + +-(void)dealloc; { +#if NON_ARC + [super dealloc]; // no-warning +#endif +} +@end + +__attribute__((objc_root_class)) +@interface NonNSObjectMissingDealloc +@property (retain) NSObject *ivar; +@end +@implementation NonNSObjectMissingDealloc +-(void)dealloc; { + +} @end -// CHECK: 4 warnings generated. diff --git a/test/Analysis/MissingDealloc.m b/test/Analysis/MissingDealloc.m index d6af44b895b..ae880546ff9 100644 --- a/test/Analysis/MissingDealloc.m +++ b/test/Analysis/MissingDealloc.m @@ -128,6 +128,9 @@ @interface SenTestCase : NSObject {} @interface MyClassTest : SenTestCase { NSString *resourcePath; } + +@property (retain) NSObject *ivar; + @end @interface NSBundle : NSObject {} @@ -143,4 +146,15 @@ - (void)testXXX { // do something which uses resourcepath } @end + +//===------------------------------------------------------------------------=== +// Don't warn for clases that aren't subclasses of NSObject + +__attribute__((objc_root_class)) +@interface NonNSObjectMissingDealloc +@property (retain) NSObject *ivar; +@end +@implementation NonNSObjectMissingDealloc +@end + // CHECK: 4 warnings generated. diff --git a/test/Analysis/PR2978.m b/test/Analysis/PR2978.m index 2067b3e85af..bb0fc414566 100644 --- a/test/Analysis/PR2978.m +++ b/test/Analysis/PR2978.m @@ -5,7 +5,7 @@ @interface NSObject - (void)release; -- dealloc; +- (void)dealloc; @end @interface MyClass : NSObject { @@ -19,8 +19,14 @@ @interface MyClass : NSObject { id _M; id _P; id _Q; + id _R; + id _S; id _V; id _W; + + MyClass *_other; + + id _nonPropertyIvar; } @property(retain) id X; @property(retain) id Y; @@ -29,8 +35,13 @@ @interface MyClass : NSObject { @property(weak) id L; @property(readonly) id N; @property(retain) id M; -@property(weak) id P; // expected-warning {{'_P' instance variable in 'MyClass' was not retained by a synthesized property but was released in 'dealloc'}} +@property(weak) id P; @property(weak) id Q; +@property(retain) id R; +@property(weak, readonly) id S; + +@property(assign, readonly) id T; // Shadowed in class extension +@property(assign) id U; @property(retain) id V; @property(retain) id W; @@ -38,36 +49,67 @@ -(id) O; -(void) setO: (id) arg; @end +@interface MyClass () +// Shadows T to make it readwrite internally but readonly externally. +@property(assign, readwrite) id T; +@end + @implementation MyClass @synthesize X = _X; -@synthesize Y = _Y; // expected-warning{{The '_Y' instance variable in 'MyClass' was retained by a synthesized property but was not released in 'dealloc'}} -@synthesize Z = _Z; // expected-warning{{The '_Z' instance variable in 'MyClass' was not retained by a synthesized property but was released in 'dealloc'}} +@synthesize Y = _Y; +@synthesize Z = _Z; @synthesize K = _K; -@synthesize L = _L; // no-warning -@synthesize N = _N; // no-warning +@synthesize L = _L; +@synthesize N = _N; @synthesize M = _M; -@synthesize Q = _Q; // expected-warning {{'_Q' instance variable in 'MyClass' was not retained by a synthesized property but was released in 'dealloc'}} +@synthesize Q = _Q; +@synthesize R = _R; @synthesize V = _V; -@synthesize W = _W; // expected-warning{{The '_W' instance variable in 'MyClass' was retained by a synthesized property but was not released in 'dealloc'}} +@synthesize W = _W; -(id) O{ return 0; } -(void) setO:(id)arg { } -- (id)dealloc + +-(void) releaseInHelper { + [_R release]; // no-warning + _R = @"Hi"; +} + +- (void)dealloc { + [_X release]; - [_Z release]; + [_Z release]; // expected-warning{{The '_Z' ivar in 'MyClass' was synthesized for an assign, readwrite property but was released in 'dealloc'}} + [_T release]; // no-warning + + [_other->_Z release]; // no-warning [_N release]; - + self.M = 0; // This will release '_M' [self setV:0]; // This will release '_V' [self setW:@"newW"]; // This will release '_W', but retain the new value - self.O = 0; // no-warning - [_Q release]; + [_S release]; // expected-warning {{The '_S' ivar in 'MyClass' was synthesized for a weak property but was released in 'dealloc'}} + + self.O = 0; // no-warning + + [_Q release]; // expected-warning {{The '_Q' ivar in 'MyClass' was synthesized for a weak property but was released in 'dealloc'}} + self.P = 0; + + [self releaseInHelper]; + + [_nonPropertyIvar release]; // no-warning + + // Silly, but not an error. + if (!_U) + [_U release]; + [super dealloc]; - return 0; + // expected-warning@-1{{The '_Y' ivar in 'MyClass' was retained by a synthesized property but not released before '[super dealloc]'}} + // expected-warning@-2{{The '_W' ivar in 'MyClass' was retained by a synthesized property but not released before '[super dealloc]'}} + } @end diff --git a/test/Analysis/properties.m b/test/Analysis/properties.m index d92f1a1a7e8..98d6d7ce1d7 100644 --- a/test/Analysis/properties.m +++ b/test/Analysis/properties.m @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount,debug.ExprInspection -analyzer-store=region -verify -Wno-objc-root-class %s -// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount,debug.ExprInspection -analyzer-store=region -verify -Wno-objc-root-class -fobjc-arc %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount,alpha.osx.cocoa.Dealloc,debug.ExprInspection -analyzer-store=region -verify -Wno-objc-root-class %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount,alpha.osx.cocoa.Dealloc,debug.ExprInspection -analyzer-store=region -verify -Wno-objc-root-class -fobjc-arc %s void clang_analyzer_eval(int); @@ -22,6 +22,7 @@ -(id)autorelease; -(id)copy; -(id)retain; -(oneway void)release; +-(void)dealloc; @end @interface NSString : NSObject - (NSUInteger)length; @@ -138,6 +139,14 @@ @interface Person : NSObject { @implementation Person @synthesize name = _name; + +-(void)dealloc { +#if !__has_feature(objc_arc) + self.name = [[NSString alloc] init]; // expected-warning {{leak}} + + [super dealloc]; // expected-warning {{The '_name' ivar in 'Person' was retained by a synthesized property but not released before '[super dealloc]}} +#endif +} @end #if !__has_feature(objc_arc) From df44b323e3044428f73abcf7b8d55f1802aa0eed Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Thu, 25 Feb 2016 19:13:43 +0000 Subject: [PATCH 226/742] Revert "[analyzer] Make ObjCDeallocChecker path sensitive." This reverts commit r261917. It broke the bots. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261921 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 425ee2e92002a79f92c86018010bf2d0a8c41e6f) --- .../Checkers/CheckObjCDealloc.cpp | 923 ++++-------------- test/Analysis/DeallocMissingRelease.m | 476 +-------- test/Analysis/MissingDealloc.m | 14 - test/Analysis/PR2978.m | 70 +- test/Analysis/properties.m | 13 +- 5 files changed, 214 insertions(+), 1282 deletions(-) diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index 7b1302aea3e..902babfe502 100644 --- a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -7,24 +7,9 @@ // //===----------------------------------------------------------------------===// // -// This checker analyzes Objective-C -dealloc methods and their callees -// to warn about improper releasing of instance variables that back synthesized -// properties. It warns about missing releases in the following cases: -// - When a class has a synthesized instance variable for a 'retain' or 'copy' -// property and lacks a -dealloc method in its implementation. -// - When a class has a synthesized instance variable for a 'retain'/'copy' -// property but the ivar is not released in -dealloc by either -release -// or by nilling out the property. -// -// It warns about extra releases in -dealloc (but not in callees) when a -// synthesized instance variable is released in the following cases: -// - When the property is 'assign' and is not 'readonly'. -// - When the property is 'weak'. -// -// This checker only warns for instance variables synthesized to back -// properties. Handling the more general case would require inferring whether -// an instance variable is stored retained or not. For synthesized properties, -// this is specified in the property declaration itself. +// This file defines a CheckObjCDealloc, a checker that +// analyzes an Objective-C class's implementation to determine if it +// correctly implements -dealloc. // //===----------------------------------------------------------------------===// @@ -35,36 +20,71 @@ #include "clang/AST/ExprObjC.h" #include "clang/Basic/LangOptions.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" #include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; -/// Indicates whether an instance variable is required to be released in -/// -dealloc. -enum class ReleaseRequirement { - /// The instance variable must be released, either by calling - /// -release on it directly or by nilling it out with a property setter. - MustRelease, +// FIXME: This was taken from IvarInvalidationChecker.cpp +static const Expr *peel(const Expr *E) { + E = E->IgnoreParenCasts(); + if (const PseudoObjectExpr *POE = dyn_cast(E)) + E = POE->getSyntacticForm()->IgnoreParenCasts(); + if (const OpaqueValueExpr *OVE = dyn_cast(E)) + E = OVE->getSourceExpr()->IgnoreParenCasts(); + return E; +} - /// The instance variable must not be directly released with -release. - MustNotReleaseDirectly, +static bool scan_ivar_release(Stmt *S, const ObjCIvarDecl *ID, + const ObjCPropertyDecl *PD, + Selector Release, + IdentifierInfo* SelfII, + ASTContext &Ctx) { + + // [mMyIvar release] + if (ObjCMessageExpr *ME = dyn_cast(S)) + if (ME->getSelector() == Release) + if (ME->getInstanceReceiver()) + if (const Expr *Receiver = peel(ME->getInstanceReceiver())) + if (auto *E = dyn_cast(Receiver)) + if (E->getDecl() == ID) + return true; + + // [self setMyIvar:nil]; + if (ObjCMessageExpr *ME = dyn_cast(S)) + if (ME->getInstanceReceiver()) + if (const Expr *Receiver = peel(ME->getInstanceReceiver())) + if (auto *E = dyn_cast(Receiver)) + if (E->getDecl()->getIdentifier() == SelfII) + if (ME->getMethodDecl() == PD->getSetterMethodDecl() && + ME->getNumArgs() == 1 && + peel(ME->getArg(0))->isNullPointerConstant(Ctx, + Expr::NPC_ValueDependentIsNull)) + return true; + + // self.myIvar = nil; + if (BinaryOperator* BO = dyn_cast(S)) + if (BO->isAssignmentOp()) + if (auto *PRE = dyn_cast(peel(BO->getLHS()))) + if (PRE->isExplicitProperty() && PRE->getExplicitProperty() == PD) + if (peel(BO->getRHS())->isNullPointerConstant(Ctx, + Expr::NPC_ValueDependentIsNull)) { + // This is only a 'release' if the property kind is not + // 'assign'. + return PD->getSetterKind() != ObjCPropertyDecl::Assign; + } + + // Recurse to children. + for (Stmt *SubStmt : S->children()) + if (SubStmt && scan_ivar_release(SubStmt, ID, PD, Release, SelfII, Ctx)) + return true; - /// The requirement for the instance variable could not be determined. - Unknown -}; + return false; +} -/// Returns true if the property implementation is synthesized and the -/// type of the property is retainable. static bool isSynthesizedRetainableProperty(const ObjCPropertyImplDecl *I, const ObjCIvarDecl **ID, const ObjCPropertyDecl **PD) { @@ -87,104 +107,33 @@ static bool isSynthesizedRetainableProperty(const ObjCPropertyImplDecl *I, return true; } -namespace { - -class ObjCDeallocChecker - : public Checker, - check::PreObjCMessage, check::PostObjCMessage, - check::BeginFunction, check::EndFunction, - check::PointerEscape, - check::PreStmt> { - - mutable IdentifierInfo *NSObjectII, *SenTestCaseII; - mutable Selector DeallocSel, ReleaseSel; - - std::unique_ptr MissingReleaseBugType; - std::unique_ptr ExtraReleaseBugType; - -public: - ObjCDeallocChecker(); - - void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr, - BugReporter &BR) const; - void checkBeginFunction(CheckerContext &Ctx) const; - void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; - void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; - - ProgramStateRef checkPointerEscape(ProgramStateRef State, - const InvalidatedSymbols &Escaped, - const CallEvent *Call, - PointerEscapeKind Kind) const; - void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const; - void checkEndFunction(CheckerContext &Ctx) const; - -private: - void diagnoseMissingReleases(CheckerContext &C) const; - - bool diagnoseExtraRelease(SymbolRef ReleasedValue, const ObjCMethodCall &M, - CheckerContext &C) const; - - SymbolRef getValueExplicitlyReleased(const ObjCMethodCall &M, - CheckerContext &C) const; - SymbolRef getValueReleasedByNillingOut(const ObjCMethodCall &M, - CheckerContext &C) const; - - SymbolRef getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym) const; - - ReleaseRequirement - getDeallocReleaseRequirement(const ObjCPropertyImplDecl *PropImpl) const; - - bool isInInstanceDealloc(const CheckerContext &C, SVal &SelfValOut) const; - bool isInInstanceDealloc(const CheckerContext &C, const LocationContext *LCtx, - SVal &SelfValOut) const; - bool instanceDeallocIsOnStack(const CheckerContext &C, - SVal &InstanceValOut) const; - - bool isSuperDeallocMessage(const ObjCMethodCall &M) const; - - const ObjCImplDecl *getContainingObjCImpl(const LocationContext *LCtx) const; - - const ObjCPropertyDecl * - findShadowedPropertyDecl(const ObjCPropertyImplDecl *PropImpl) const; - - ProgramStateRef removeValueRequiringRelease(ProgramStateRef State, - SymbolRef InstanceSym, - SymbolRef ValueSym) const; - - void initIdentifierInfoAndSelectors(ASTContext &Ctx) const; - - bool classHasSeparateTeardown(const ObjCInterfaceDecl *ID) const; -}; -} // End anonymous namespace. - -typedef llvm::ImmutableSet SymbolSet; - -/// Maps from the symbol for a class instance to the set of -/// symbols remaining that must be released in -dealloc. -REGISTER_MAP_WITH_PROGRAMSTATE(UnreleasedIvarMap, SymbolRef, SymbolSet); - -template<> struct ProgramStateTrait -: public ProgramStatePartialTrait { - static void *GDMIndex() { static int index = 0; return &index; } -}; +static bool synthesizedPropertyRequiresRelease(const ObjCPropertyDecl *PD) { + // A synthesized property must be released if and only if the kind of setter + // was neither 'assign' or 'weak'. + ObjCPropertyDecl::SetterKind SK = PD->getSetterKind(); + return (SK != ObjCPropertyDecl::Assign && SK != ObjCPropertyDecl::Weak); +} +static void checkObjCDealloc(const CheckerBase *Checker, + const ObjCImplementationDecl *D, + const LangOptions &LOpts, BugReporter &BR) { -/// An AST check that diagnose when the class requires a -dealloc method and -/// is missing one. -void ObjCDeallocChecker::checkASTDecl(const ObjCImplementationDecl *D, - AnalysisManager &Mgr, - BugReporter &BR) const { - assert(Mgr.getLangOpts().getGC() != LangOptions::GCOnly); - assert(!Mgr.getLangOpts().ObjCAutoRefCount); - initIdentifierInfoAndSelectors(Mgr.getASTContext()); + assert(LOpts.getGC() != LangOptions::GCOnly); + assert(!LOpts.ObjCAutoRefCount); + ASTContext &Ctx = BR.getContext(); const ObjCInterfaceDecl *ID = D->getClassInterface(); // Does the class contain any synthesized properties that are retainable? // If not, skip the check entirely. bool containsRetainedSynthesizedProperty = false; for (const auto *I : D->property_impls()) { - if (getDeallocReleaseRequirement(I) == ReleaseRequirement::MustRelease) { + const ObjCIvarDecl *ID = nullptr; + const ObjCPropertyDecl *PD = nullptr; + if (!isSynthesizedRetainableProperty(I, &ID, &PD)) + continue; + + if (synthesizedPropertyRequiresRelease(PD)) { containsRetainedSynthesizedProperty = true; break; } @@ -193,658 +142,136 @@ void ObjCDeallocChecker::checkASTDecl(const ObjCImplementationDecl *D, if (!containsRetainedSynthesizedProperty) return; - // If the class is known to have a lifecycle with a separate teardown method - // then it may not require a -dealloc method. - if (classHasSeparateTeardown(ID)) + // Determine if the class subclasses NSObject. + IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject"); + IdentifierInfo* SenTestCaseII = &Ctx.Idents.get("SenTestCase"); + + for ( ; ID ; ID = ID->getSuperClass()) { + IdentifierInfo *II = ID->getIdentifier(); + + if (II == NSObjectII) + break; + + // FIXME: For now, ignore classes that subclass SenTestCase, as these don't + // need to implement -dealloc. They implement tear down in another way, + // which we should try and catch later. + // http://llvm.org/bugs/show_bug.cgi?id=3187 + if (II == SenTestCaseII) + return; + } + + if (!ID) return; + // Get the "dealloc" selector. + IdentifierInfo* II = &Ctx.Idents.get("dealloc"); + Selector S = Ctx.Selectors.getSelector(0, &II); const ObjCMethodDecl *MD = nullptr; // Scan the instance methods for "dealloc". for (const auto *I : D->instance_methods()) { - if (I->getSelector() == DeallocSel) { + if (I->getSelector() == S) { MD = I; break; } } if (!MD) { // No dealloc found. - const char* Name = "Missing -dealloc"; - std::string Buf; - llvm::raw_string_ostream OS(Buf); - OS << "Objective-C class '" << *D << "' lacks a 'dealloc' instance method"; + const char* name = LOpts.getGC() == LangOptions::NonGC + ? "missing -dealloc" + : "missing -dealloc (Hybrid MM, non-GC)"; + + std::string buf; + llvm::raw_string_ostream os(buf); + os << "Objective-C class '" << *D << "' lacks a 'dealloc' instance method"; PathDiagnosticLocation DLoc = PathDiagnosticLocation::createBegin(D, BR.getSourceManager()); - BR.EmitBasicReport(D, this, Name, categories::CoreFoundationObjectiveC, - OS.str(), DLoc); - return; - } -} - -/// If this is the beginning of -dealloc, mark the values initially stored in -/// instance variables that must be released by the end of -dealloc -/// as unreleased in the state. -void ObjCDeallocChecker::checkBeginFunction( - CheckerContext &C) const { - initIdentifierInfoAndSelectors(C.getASTContext()); - - // Only do this if the current method is -dealloc. - SVal SelfVal; - if (!isInInstanceDealloc(C, SelfVal)) - return; - - SymbolRef SelfSymbol = SelfVal.getAsSymbol(); - - const LocationContext *LCtx = C.getLocationContext(); - ProgramStateRef InitialState = C.getState(); - - ProgramStateRef State = InitialState; - - SymbolSet::Factory &F = State->getStateManager().get_context(); - - // Symbols that must be released by the end of the -dealloc; - SymbolSet RequiredReleases = F.getEmptySet(); - - // If we're an inlined -dealloc, we should add our symbols to the existing - // set from our subclass. - if (const SymbolSet *CurrSet = State->get(SelfSymbol)) - RequiredReleases = *CurrSet; - - for (auto *PropImpl : getContainingObjCImpl(LCtx)->property_impls()) { - ReleaseRequirement Requirement = getDeallocReleaseRequirement(PropImpl); - if (Requirement != ReleaseRequirement::MustRelease) - continue; - - SVal LVal = State->getLValue(PropImpl->getPropertyIvarDecl(), SelfVal); - Optional LValLoc = LVal.getAs(); - if (!LValLoc) - continue; - - SVal InitialVal = State->getSVal(LValLoc.getValue()); - SymbolRef Symbol = InitialVal.getAsSymbol(); - if (!Symbol || !isa(Symbol)) - continue; - - // Mark the value as requiring a release. - RequiredReleases = F.add(RequiredReleases, Symbol); - } - - if (!RequiredReleases.isEmpty()) { - State = State->set(SelfSymbol, RequiredReleases); - } - - if (State != InitialState) { - C.addTransition(State); - } -} - -/// Given a symbol for an ivar, return a symbol for the instance containing -/// the ivar. Returns nullptr if the instance symbol cannot be found. -SymbolRef -ObjCDeallocChecker::getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym) const { - if (auto *SRV = dyn_cast(IvarSym)) { - const TypedValueRegion *TVR = SRV->getRegion(); - const ObjCIvarRegion *IvarRegion = dyn_cast_or_null(TVR); - if (!IvarRegion) - return nullptr; - - return IvarRegion->getSymbolicBase()->getSymbol(); - } - - return nullptr; -} - -/// If we are in -dealloc or -dealloc is on the stack, handle the call if it is -/// a release or a nilling-out property setter. -void ObjCDeallocChecker::checkPreObjCMessage( - const ObjCMethodCall &M, CheckerContext &C) const { - // Only run if -dealloc is on the stack. - SVal DeallocedInstance; - if (!instanceDeallocIsOnStack(C, DeallocedInstance)) + BR.EmitBasicReport(D, Checker, name, categories::CoreFoundationObjectiveC, + os.str(), DLoc); return; - - SymbolRef ReleasedValue = getValueExplicitlyReleased(M, C); - - if (ReleasedValue) { - // An instance variable symbol was released with -release: - // [_property release]; - if (diagnoseExtraRelease(ReleasedValue,M, C)) - return; - } else { - // An instance variable symbol was released nilling out its property: - // self.property = nil; - ReleasedValue = getValueReleasedByNillingOut(M, C); - } - - if (!ReleasedValue) - return; - - SymbolRef InstanceSym = getInstanceSymbolFromIvarSymbol(ReleasedValue); - if (!InstanceSym) - return; - ProgramStateRef InitialState = C.getState(); - - ProgramStateRef ReleasedState = - removeValueRequiringRelease(InitialState, InstanceSym, ReleasedValue); - - if (ReleasedState != InitialState) { - C.addTransition(ReleasedState); - } -} - -/// If the message was a call to '[super dealloc]', diagnose any missing -/// releases. -void ObjCDeallocChecker::checkPostObjCMessage( - const ObjCMethodCall &M, CheckerContext &C) const { - // We perform this check post-message so that if the super -dealloc - // calls a helper method and that this class overrides, any ivars released in - // the helper method will be recorded before checking. - if (isSuperDeallocMessage(M)) - diagnoseMissingReleases(C); -} - -/// Check for missing releases even when -dealloc does not call -/// '[super dealloc]'. -void ObjCDeallocChecker::checkEndFunction( - CheckerContext &C) const { - diagnoseMissingReleases(C); -} - -/// Check for missing releases on early return. -void ObjCDeallocChecker::checkPreStmt( - const ReturnStmt *RS, CheckerContext &C) const { - diagnoseMissingReleases(C); -} - -/// If a symbol escapes conservatively assume unseen code released it. -ProgramStateRef ObjCDeallocChecker::checkPointerEscape( - ProgramStateRef State, const InvalidatedSymbols &Escaped, - const CallEvent *Call, PointerEscapeKind Kind) const { - - // Don't treat calls to '[super dealloc]' as escaping for the purposes - // of this checker. Because the checker diagnoses missing releases in the - // post-message handler for '[super dealloc], escaping here would cause - // the checker to never warn. - auto *OMC = dyn_cast_or_null(Call); - if (OMC && isSuperDeallocMessage(*OMC)) - return State; - - for (const auto &Sym : Escaped) { - State = State->remove(Sym); - - SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(Sym); - if (!InstanceSymbol) - continue; - - State = removeValueRequiringRelease(State, InstanceSymbol, Sym); } - return State; -} - -/// Report any unreleased instance variables for the current instance being -/// dealloced. -void ObjCDeallocChecker::diagnoseMissingReleases(CheckerContext &C) const { - ProgramStateRef State = C.getState(); - - SVal SelfVal; - if (!isInInstanceDealloc(C, SelfVal)) - return; + // Get the "release" selector. + IdentifierInfo* RII = &Ctx.Idents.get("release"); + Selector RS = Ctx.Selectors.getSelector(0, &RII); - const MemRegion *SelfRegion = SelfVal.castAs().getRegion(); - const LocationContext *LCtx = C.getLocationContext(); + // Get the "self" identifier + IdentifierInfo* SelfII = &Ctx.Idents.get("self"); - ExplodedNode *ErrNode = nullptr; - - SymbolRef SelfSym = SelfVal.getAsSymbol(); - if (!SelfSym) - return; - - const SymbolSet *OldUnreleased = State->get(SelfSym); - if (!OldUnreleased) - return; - - SymbolSet NewUnreleased = *OldUnreleased; - SymbolSet::Factory &F = State->getStateManager().get_context(); - - ProgramStateRef InitialState = State; - - for (auto *IvarSymbol : *OldUnreleased) { - const TypedValueRegion *TVR = - cast(IvarSymbol)->getRegion(); - const ObjCIvarRegion *IvarRegion = cast(TVR); - - // Don't warn if the ivar is not for this instance. - if (SelfRegion != IvarRegion->getSuperRegion()) - continue; - - // Prevent an inlined call to -dealloc in a super class from warning - // about the values the subclass's -dealloc should release. - if (IvarRegion->getDecl()->getContainingInterface() != - cast(LCtx->getDecl())->getClassInterface()) - continue; - - // Prevents diagnosing multiple times for the same instance variable - // at, for example, both a return and at the end of of the function. - NewUnreleased = F.remove(NewUnreleased, IvarSymbol); - - if (State->getStateManager() - .getConstraintManager() - .isNull(State, IvarSymbol) - .isConstrainedTrue()) { - continue; - } - - // A missing release manifests as a leak, so treat as a non-fatal error. - if (!ErrNode) - ErrNode = C.generateNonFatalErrorNode(); - // If we've already reached this node on another path, return without - // diagnosing. - if (!ErrNode) - return; - - std::string Buf; - llvm::raw_string_ostream OS(Buf); - - const ObjCIvarDecl *IvarDecl = IvarRegion->getDecl(); - const ObjCInterfaceDecl *Interface = IvarDecl->getContainingInterface(); - // If the class is known to have a lifecycle with teardown that is - // separate from -dealloc, do not warn about missing releases. We - // suppress here (rather than not tracking for instance variables in - // such classes) because these classes are rare. - if (classHasSeparateTeardown(Interface)) - return; - - ObjCImplDecl *ImplDecl = Interface->getImplementation(); - - const ObjCPropertyImplDecl *PropImpl = - ImplDecl->FindPropertyImplIvarDecl(IvarDecl->getIdentifier()); - - const ObjCPropertyDecl *PropDecl = PropImpl->getPropertyDecl(); - - assert(PropDecl->getSetterKind() == ObjCPropertyDecl::Copy || - PropDecl->getSetterKind() == ObjCPropertyDecl::Retain); - - OS << "The '" << *IvarDecl << "' ivar in '" << *ImplDecl - << "' was "; - - if (PropDecl->getSetterKind() == ObjCPropertyDecl::Retain) - OS << "retained"; - else - OS << "copied"; - - OS << " by a synthesized property but not released" - " before '[super dealloc]'"; - - std::unique_ptr BR( - new BugReport(*MissingReleaseBugType, OS.str(), ErrNode)); - - C.emitReport(std::move(BR)); - } - - if (NewUnreleased.isEmpty()) { - State = State->remove(SelfSym); - } else { - State = State->set(SelfSym, NewUnreleased); - } - - if (ErrNode) { - C.addTransition(State, ErrNode); - } else if (State != InitialState) { - C.addTransition(State); - } - - // Make sure that after checking in the top-most frame the list of - // tracked ivars is empty. This is intended to detect accidental leaks in - // the UnreleasedIvarMap program state. - assert(!LCtx->inTopFrame() || State->get().isEmpty()); -} - -/// Emits a warning if the current context is -dealloc and \param ReleasedValue -/// must not be directly released in a -dealloc. Returns true if a diagnostic -/// was emitted. -bool ObjCDeallocChecker::diagnoseExtraRelease(SymbolRef ReleasedValue, - const ObjCMethodCall &M, - CheckerContext &C) const { - SVal DeallocedInstance; - if (!isInInstanceDealloc(C, DeallocedInstance)) - return false; - - // Try to get the region from which the the released value was loaded. - // Note that, unlike diagnosing for missing releases, here we don't track - // values that must not be released in the state. This is because even if - // these values escape, it is still an error under the rules of MRR to - // release them in -dealloc. - const MemRegion *RegionLoadedFrom = nullptr; - if (auto *DerivedSym = dyn_cast(ReleasedValue)) - RegionLoadedFrom = DerivedSym->getRegion(); - else if (auto *RegionSym = dyn_cast(ReleasedValue)) - RegionLoadedFrom = RegionSym->getRegion(); - else - return false; - - auto *ReleasedIvar = dyn_cast(RegionLoadedFrom); - if (!ReleasedIvar) - return false; - - if (DeallocedInstance.castAs().getRegion() != - ReleasedIvar->getSuperRegion()) - return false; - - const LocationContext *LCtx = C.getLocationContext(); - const ObjCIvarDecl *ReleasedIvarDecl = ReleasedIvar->getDecl(); - - // If the ivar belongs to a property that must not be released directly - // in dealloc, emit a warning. - const ObjCImplDecl *Container = getContainingObjCImpl(LCtx); - const ObjCPropertyImplDecl *PropImpl = - Container->FindPropertyImplIvarDecl(ReleasedIvarDecl->getIdentifier()); - if (!PropImpl) - return false; - - if (getDeallocReleaseRequirement(PropImpl) != - ReleaseRequirement::MustNotReleaseDirectly) { - return false; - } - - // If the property is readwrite but it shadows a read-only property in its - // external interface, treat the property a read-only. If the outside - // world cannot write to a property then the internal implementation is free - // to make its own convention about whether the value is stored retained - // or not. We look up the shadow here rather than in - // getDeallocReleaseRequirement() because doing so can be expensive. - const ObjCPropertyDecl *PropDecl = findShadowedPropertyDecl(PropImpl); - if (PropDecl) { - if (PropDecl->isReadOnly()) - return false; - } else { - PropDecl = PropImpl->getPropertyDecl(); - } - - ExplodedNode *ErrNode = C.generateNonFatalErrorNode(); - if (!ErrNode) - return false; - - std::string Buf; - llvm::raw_string_ostream OS(Buf); - - assert(PropDecl->getSetterKind() == ObjCPropertyDecl::Weak || - (PropDecl->getSetterKind() == ObjCPropertyDecl::Assign && - !PropDecl->isReadOnly())); - - OS << "The '" << *PropImpl->getPropertyIvarDecl() - << "' ivar in '" << *Container - << "' was synthesized for "; - - if (PropDecl->getSetterKind() == ObjCPropertyDecl::Weak) - OS << "a weak"; - else - OS << "an assign, readwrite"; - - OS << " property but was released in 'dealloc'"; - - std::unique_ptr BR( - new BugReport(*ExtraReleaseBugType, OS.str(), ErrNode)); - BR->addRange(M.getOriginExpr()->getSourceRange()); - - C.emitReport(std::move(BR)); - - return true; -} - - -ObjCDeallocChecker:: - ObjCDeallocChecker() - : NSObjectII(nullptr), SenTestCaseII(nullptr) { - - MissingReleaseBugType.reset( - new BugType(this, "Missing ivar release (leak)", - categories::MemoryCoreFoundationObjectiveC)); - - ExtraReleaseBugType.reset( - new BugType(this, "Extra ivar release", - categories::MemoryCoreFoundationObjectiveC)); -} - -void ObjCDeallocChecker::initIdentifierInfoAndSelectors( - ASTContext &Ctx) const { - if (NSObjectII) - return; - - NSObjectII = &Ctx.Idents.get("NSObject"); - SenTestCaseII = &Ctx.Idents.get("SenTestCase"); - - IdentifierInfo *DeallocII = &Ctx.Idents.get("dealloc"); - IdentifierInfo *ReleaseII = &Ctx.Idents.get("release"); - DeallocSel = Ctx.Selectors.getSelector(0, &DeallocII); - ReleaseSel = Ctx.Selectors.getSelector(0, &ReleaseII); -} - -/// Returns true if \param M is a call to '[super dealloc]'. -bool ObjCDeallocChecker::isSuperDeallocMessage( - const ObjCMethodCall &M) const { - if (M.getOriginExpr()->getReceiverKind() != ObjCMessageExpr::SuperInstance) - return false; - - return M.getSelector() == DeallocSel; -} - -/// Returns the ObjCImplDecl containing the method declaration in \param LCtx. -const ObjCImplDecl * -ObjCDeallocChecker::getContainingObjCImpl(const LocationContext *LCtx) const { - auto *MD = cast(LCtx->getDecl()); - return cast(MD->getDeclContext()); -} - -/// Returns the property that shadowed by \param PropImpl if one exists and -/// nullptr otherwise. -const ObjCPropertyDecl *ObjCDeallocChecker::findShadowedPropertyDecl( - const ObjCPropertyImplDecl *PropImpl) const { - const ObjCPropertyDecl *PropDecl = PropImpl->getPropertyDecl(); - - // Only readwrite properties can shadow. - if (PropDecl->isReadOnly()) - return nullptr; - - auto *CatDecl = dyn_cast(PropDecl->getDeclContext()); - - // Only class extensions can contain shadowing properties. - if (!CatDecl || !CatDecl->IsClassExtension()) - return nullptr; - - IdentifierInfo *ID = PropDecl->getIdentifier(); - DeclContext::lookup_result R = CatDecl->getClassInterface()->lookup(ID); - for (DeclContext::lookup_iterator I = R.begin(), E = R.end(); I != E; ++I) { - auto *ShadowedPropDecl = dyn_cast(*I); - if (!ShadowedPropDecl) + // Scan for missing and extra releases of ivars used by implementations + // of synthesized properties + for (const auto *I : D->property_impls()) { + const ObjCIvarDecl *ID = nullptr; + const ObjCPropertyDecl *PD = nullptr; + if (!isSynthesizedRetainableProperty(I, &ID, &PD)) continue; - if (ShadowedPropDecl->isInstanceProperty()) { - assert(ShadowedPropDecl->isReadOnly()); - return ShadowedPropDecl; + bool requiresRelease = synthesizedPropertyRequiresRelease(PD); + if (scan_ivar_release(MD->getBody(), ID, PD, RS, SelfII, Ctx) + != requiresRelease) { + const char *name = nullptr; + std::string buf; + llvm::raw_string_ostream os(buf); + + if (requiresRelease) { + name = LOpts.getGC() == LangOptions::NonGC + ? "missing ivar release (leak)" + : "missing ivar release (Hybrid MM, non-GC)"; + + os << "The '" << *ID << "' instance variable in '" << *D + << "' was retained by a synthesized property " + "but was not released in 'dealloc'"; + } else { + // It is common for the ivars for read-only assign properties to + // always be stored retained, so don't warn for a release in + // dealloc for the ivar backing these properties. + if (PD->isReadOnly()) + continue; + + name = LOpts.getGC() == LangOptions::NonGC + ? "extra ivar release (use-after-release)" + : "extra ivar release (Hybrid MM, non-GC)"; + + os << "The '" << *ID << "' instance variable in '" << *D + << "' was not retained by a synthesized property " + "but was released in 'dealloc'"; + } + + // If @synthesize statement is missing, fall back to @property statement. + const Decl *SPDecl = I->getLocation().isValid() + ? static_cast(I) + : static_cast(PD); + PathDiagnosticLocation SPLoc = + PathDiagnosticLocation::createBegin(SPDecl, BR.getSourceManager()); + + BR.EmitBasicReport(MD, Checker, name, + categories::CoreFoundationObjectiveC, os.str(), SPLoc); } } - - return nullptr; } -/// Remove the \param Value requiring a release from the tracked set for -/// \param Instance and return the resultant state. -ProgramStateRef ObjCDeallocChecker::removeValueRequiringRelease( - ProgramStateRef State, SymbolRef Instance, SymbolRef Value) const { - assert(Instance); - assert(Value); - - const SymbolSet *Unreleased = State->get(Instance); - if (!Unreleased) - return State; - - // Mark the value as no longer requiring a release. - SymbolSet::Factory &F = State->getStateManager().get_context(); - SymbolSet NewUnreleased = F.remove(*Unreleased, Value); - - if (NewUnreleased.isEmpty()) { - return State->remove(Instance); - } - - return State->set(Instance, NewUnreleased); -} - -/// Determines whether the instance variable for \p PropImpl must or must not be -/// released in -dealloc or whether it cannot be determined. -ReleaseRequirement ObjCDeallocChecker::getDeallocReleaseRequirement( - const ObjCPropertyImplDecl *PropImpl) const { - const ObjCIvarDecl *IvarDecl; - const ObjCPropertyDecl *PropDecl; - if (!isSynthesizedRetainableProperty(PropImpl, &IvarDecl, &PropDecl)) - return ReleaseRequirement::Unknown; - - ObjCPropertyDecl::SetterKind SK = PropDecl->getSetterKind(); - - switch (SK) { - // Retain and copy setters retain/copy their values before storing and so - // the value in their instance variables must be released in -dealloc. - case ObjCPropertyDecl::Retain: - case ObjCPropertyDecl::Copy: - return ReleaseRequirement::MustRelease; - - case ObjCPropertyDecl::Weak: - return ReleaseRequirement::MustNotReleaseDirectly; - - case ObjCPropertyDecl::Assign: - // It is common for the ivars for read-only assign properties to - // always be stored retained, so their release requirement cannot be - // be determined. - if (PropDecl->isReadOnly()) - return ReleaseRequirement::Unknown; - - return ReleaseRequirement::MustNotReleaseDirectly; - } -} - -/// Returns the released value if \param M is a call to -release. Returns -/// nullptr otherwise. -SymbolRef -ObjCDeallocChecker::getValueExplicitlyReleased(const ObjCMethodCall &M, - CheckerContext &C) const { - if (M.getSelector() != ReleaseSel) - return nullptr; - - return M.getReceiverSVal().getAsSymbol(); -} - -/// Returns the released value if \param M is a call a setter that releases -/// and nils out its underlying instance variable. -SymbolRef -ObjCDeallocChecker::getValueReleasedByNillingOut(const ObjCMethodCall &M, - CheckerContext &C) const { - SVal ReceiverVal = M.getReceiverSVal(); - if (!ReceiverVal.isValid()) - return nullptr; - - // Is the first argument nil? - if (M.getNumArgs() == 0) - return nullptr; - SVal Arg = M.getArgSVal(0); - ProgramStateRef notNilState, nilState; - std::tie(notNilState, nilState) = - M.getState()->assume(Arg.castAs()); - if (!(nilState && !notNilState)) - return nullptr; - - const ObjCPropertyDecl *Prop = M.getAccessedProperty(); - if (!Prop) - return nullptr; - - ObjCIvarDecl *PropIvarDecl = Prop->getPropertyIvarDecl(); - if (!PropIvarDecl) - return nullptr; - - ProgramStateRef State = C.getState(); - - SVal LVal = State->getLValue(PropIvarDecl, ReceiverVal); - Optional LValLoc = LVal.getAs(); - if (!LValLoc) - return nullptr; - - SVal CurrentValInIvar = State->getSVal(LValLoc.getValue()); - return CurrentValInIvar.getAsSymbol(); -} - -/// Returns true if the current context is a call to -dealloc and false -/// otherwise. If true, it also sets \param SelfValOut to the value of -/// 'self'. -bool ObjCDeallocChecker::isInInstanceDealloc(const CheckerContext &C, - SVal &SelfValOut) const { - return isInInstanceDealloc(C, C.getLocationContext(), SelfValOut); -} - -/// Returns true if \param LCtx is a call to -dealloc and false -/// otherwise. If true, it also sets \param SelfValOut to the value of -/// 'self'. -bool ObjCDeallocChecker::isInInstanceDealloc(const CheckerContext &C, - const LocationContext *LCtx, - SVal &SelfValOut) const { - auto *MD = dyn_cast(LCtx->getDecl()); - if (!MD || !MD->isInstanceMethod() || MD->getSelector() != DeallocSel) - return false; - - const ImplicitParamDecl *SelfDecl = LCtx->getSelfDecl(); - assert(SelfDecl && "No self in -dealloc?"); - - ProgramStateRef State = C.getState(); - SelfValOut = State->getSVal(State->getRegion(SelfDecl, LCtx)); - return true; -} - -/// Returns true if there is a call to -dealloc anywhere on the stack and false -/// otherwise. If true, it also sets \param InstanceValOut to the value of -/// 'self' in the frame for -dealloc. -bool ObjCDeallocChecker::instanceDeallocIsOnStack(const CheckerContext &C, - SVal &InstanceValOut) const { - const LocationContext *LCtx = C.getLocationContext(); - - while (LCtx) { - if (isInInstanceDealloc(C, LCtx, InstanceValOut)) - return true; - - LCtx = LCtx->getParent(); - } - - return false; -} - -/// Returns true if the \param ID is a class in which which is known to have -/// a separate teardown lifecycle. In this case, -dealloc warnings -/// about missing releases should be suppressed. -bool ObjCDeallocChecker::classHasSeparateTeardown( - const ObjCInterfaceDecl *ID) const { - // Suppress if the class is not a subclass of NSObject. - for ( ; ID ; ID = ID->getSuperClass()) { - IdentifierInfo *II = ID->getIdentifier(); - - if (II == NSObjectII) - return false; +//===----------------------------------------------------------------------===// +// ObjCDeallocChecker +//===----------------------------------------------------------------------===// - // FIXME: For now, ignore classes that subclass SenTestCase, as these don't - // need to implement -dealloc. They implement tear down in another way, - // which we should try and catch later. - // http://llvm.org/bugs/show_bug.cgi?id=3187 - if (II == SenTestCaseII) - return true; +namespace { +class ObjCDeallocChecker : public Checker< + check::ASTDecl > { +public: + void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr, + BugReporter &BR) const { + if (mgr.getLangOpts().getGC() == LangOptions::GCOnly || + mgr.getLangOpts().ObjCAutoRefCount) + return; + checkObjCDealloc(this, cast(D), mgr.getLangOpts(), + BR); } - - return true; +}; } -void ento::registerObjCDeallocChecker(CheckerManager &Mgr) { - const LangOptions &LangOpts = Mgr.getLangOpts(); - // These checker only makes sense under MRR. - if (LangOpts.getGC() == LangOptions::GCOnly || LangOpts.ObjCAutoRefCount) - return; - - Mgr.registerChecker(); +void ento::registerObjCDeallocChecker(CheckerManager &mgr) { + mgr.registerChecker(); } diff --git a/test/Analysis/DeallocMissingRelease.m b/test/Analysis/DeallocMissingRelease.m index c7769db3d5f..e782f9968f9 100644 --- a/test/Analysis/DeallocMissingRelease.m +++ b/test/Analysis/DeallocMissingRelease.m @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.osx.cocoa.Dealloc -fblocks -verify %s -// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.osx.cocoa.Dealloc -fblocks -triple x86_64-apple-darwin10 -fobjc-arc -fobjc-runtime-has-weak -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.osx.cocoa.Dealloc -fblocks %s 2>&1 | FileCheck -check-prefix=CHECK %s +// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.osx.cocoa.Dealloc -fblocks -triple x86_64-apple-darwin10 -fobjc-arc -fobjc-runtime-has-weak %s 2>&1 | FileCheck -check-prefix=CHECK-ARC -allow-empty '--implicit-check-not=error:' '--implicit-check-not=warning:' %s #define nil ((id)0) @@ -11,11 +11,6 @@ #define WEAK_ON_ARC __weak #endif -// No diagnostics expected under ARC. -#if !NON_ARC - // expected-no-diagnostics -#endif - typedef signed char BOOL; @protocol NSObject - (BOOL)isEqual:(id)object; @@ -23,7 +18,6 @@ - (Class)class; @end @interface NSObject {} -+ (instancetype)alloc; - (void)dealloc; - (id)init; - (id)retain; @@ -32,6 +26,7 @@ - (oneway void)release; typedef struct objc_selector *SEL; +//===------------------------------------------------------------------------=== // Do not warn about missing release in -dealloc for ivars. @interface MyIvarClass1 : NSObject { @@ -92,9 +87,11 @@ - (void)setIvar:(NSObject *)ivar } @end +//===------------------------------------------------------------------------=== // Warn about missing release in -dealloc for properties. @interface MyPropertyClass1 : NSObject +// CHECK: DeallocMissingRelease.m:[[@LINE+1]]:1: warning: The '_ivar' instance variable in 'MyPropertyClass1' was retained by a synthesized property but was not released in 'dealloc' @property (copy) NSObject *ivar; @end @@ -102,12 +99,13 @@ @implementation MyPropertyClass1 - (void)dealloc { #if NON_ARC - [super dealloc]; // expected-warning {{The '_ivar' ivar in 'MyPropertyClass1' was copied by a synthesized property but not released before '[super dealloc]'}} + [super dealloc]; #endif } @end @interface MyPropertyClass2 : NSObject +// CHECK: DeallocMissingRelease.m:[[@LINE+1]]:1: warning: The '_ivar' instance variable in 'MyPropertyClass2' was retained by a synthesized property but was not released in 'dealloc' @property (retain) NSObject *ivar; @end @@ -115,7 +113,7 @@ @implementation MyPropertyClass2 - (void)dealloc { #if NON_ARC - [super dealloc]; // expected-warning {{The '_ivar' ivar in 'MyPropertyClass2' was retained by a synthesized property but not released before '[super dealloc]'}} + [super dealloc]; #endif } @end @@ -127,14 +125,14 @@ @interface MyPropertyClass3 : NSObject { @end @implementation MyPropertyClass3 +// CHECK: DeallocMissingRelease.m:[[@LINE+1]]:1: warning: The '_ivar' instance variable in 'MyPropertyClass3' was retained by a synthesized property but was not released in 'dealloc' @synthesize ivar = _ivar; - (void)dealloc { #if NON_ARC - [super dealloc]; // expected-warning {{The '_ivar' ivar in 'MyPropertyClass3' was retained by a synthesized property but not released before '[super dealloc]'}} + [super dealloc]; #endif } - @end @interface MyPropertyClass4 : NSObject { @@ -144,11 +142,12 @@ @interface MyPropertyClass4 : NSObject { @end @implementation MyPropertyClass4 +// CHECK: DeallocMissingRelease.m:[[@LINE+1]]:1: warning: The '_blockPropertyIvar' instance variable in 'MyPropertyClass4' was retained by a synthesized property but was not released in 'dealloc' @synthesize blockProperty = _blockPropertyIvar; - (void)dealloc { #if NON_ARC - [super dealloc]; // expected-warning {{The '_blockPropertyIvar' ivar in 'MyPropertyClass4' was copied by a synthesized property but not released before '[super dealloc]'}} + [super dealloc]; #endif } @end @@ -160,77 +159,16 @@ @interface MyPropertyClass5 : NSObject { @end @implementation MyPropertyClass5 -@synthesize ivar = _ivar; +@synthesize ivar = _ivar; // no-warning - (void)dealloc { #if NON_ARC - [super dealloc]; // no-warning because it is a weak property -#endif -} -@end - -@interface MyPropertyClassWithReturnInDealloc : NSObject { - NSObject *_ivar; -} -@property (retain) NSObject *ivar; -@end - -@implementation MyPropertyClassWithReturnInDealloc -@synthesize ivar = _ivar; -- (void)dealloc -{ - return; -#if NON_ARC - // expected-warning@-2{{The '_ivar' ivar in 'MyPropertyClassWithReturnInDealloc' was retained by a synthesized property but not released before '[super dealloc]'}} [super dealloc]; #endif } @end -@interface MyPropertyClassWithReleaseInOtherInstance : NSObject { - NSObject *_ivar; - MyPropertyClassWithReleaseInOtherInstance *_other; -} -@property (retain) NSObject *ivar; - --(void)releaseIvars; -@end - -@implementation MyPropertyClassWithReleaseInOtherInstance -@synthesize ivar = _ivar; - --(void)releaseIvars; { -#if NON_ARC - [_ivar release]; -#endif -} - -- (void)dealloc -{ - [_other releaseIvars]; -#if NON_ARC - [super dealloc]; // expected-warning {{The '_ivar' ivar in 'MyPropertyClassWithReleaseInOtherInstance' was retained by a synthesized property but not released before '[super dealloc]'}} -#endif -} -@end - -@interface MyPropertyClassWithNeitherReturnNorSuperDealloc : NSObject { - NSObject *_ivar; -} -@property (retain) NSObject *ivar; -@end - -@implementation MyPropertyClassWithNeitherReturnNorSuperDealloc -@synthesize ivar = _ivar; -- (void)dealloc -{ -} -#if NON_ARC - // expected-warning@-2 {{method possibly missing a [super dealloc] call}} (From Sema) - // expected-warning@-3{{The '_ivar' ivar in 'MyPropertyClassWithNeitherReturnNorSuperDealloc' was retained by a synthesized property but not released before '[super dealloc]'}} -#endif -@end - +//===------------------------------------------------------------------------=== // : 'myproperty' has kind 'assign' and thus the // assignment through the setter does not perform a release. @@ -264,419 +202,51 @@ - (void)dealloc; { // We really should warn because there is a path through -dealloc on which // _ivar2 is not released. #if NON_ARC - [_ivar2 release]; + [_ivar2 release]; // no-warning #endif } #if NON_ARC - [super dealloc]; // expected-warning {{The '_ivar2' ivar in 'ClassWithControlFlowInRelease' was retained by a synthesized property but not released before '[super dealloc]'}} + [super dealloc]; #endif } + @end +//===------------------------------------------------------------------------=== // Don't warn when the property is nil'd out in -dealloc @interface ClassWithNildOutProperty : NSObject -@property (retain) NSObject *ivar; -@property (assign) int *intPtrProp; +@property (retain) NSObject *ivar; // no-warning @end @implementation ClassWithNildOutProperty - (void)dealloc; { self.ivar = nil; - // Make sure to handle setting a non-retainable property to 0. - self.intPtrProp = 0; -#if NON_ARC - [super dealloc]; // no-warning -#endif -} -@end - -// Do warn when the ivar but not the property is nil'd out in -dealloc - -@interface ClassWithNildOutIvar : NSObject -@property (retain) NSObject *ivar; -@end - -@implementation ClassWithNildOutIvar -- (void)dealloc; { - // Oops. Meant self.ivar = nil - _ivar = nil; - #if NON_ARC - [super dealloc]; // expected-warning {{The '_ivar' ivar in 'ClassWithNildOutIvar' was retained by a synthesized property but not released before '[super dealloc]'}} + [super dealloc]; #endif } -@end -// Do warn when the ivar is updated to a different value that is then -// released. - -@interface ClassWithUpdatedIvar : NSObject -@property (retain) NSObject *ivar; @end -@implementation ClassWithUpdatedIvar -- (void)dealloc; { - _ivar = [[NSObject alloc] init]; - -#if NON_ARC - [_ivar release]; -#endif - -#if NON_ARC - [super dealloc]; // expected-warning {{The '_ivar' ivar in 'ClassWithUpdatedIvar' was retained by a synthesized property but not released before '[super dealloc]'}} -#endif -} -@end - - +//===------------------------------------------------------------------------=== // Don't warn when the property is nil'd out with a setter in -dealloc @interface ClassWithNildOutPropertyViaSetter : NSObject -@property (retain) NSObject *ivar; +@property (retain) NSObject *ivar; // no-warning @end @implementation ClassWithNildOutPropertyViaSetter - (void)dealloc; { [self setIvar:nil]; -#if NON_ARC - [super dealloc]; // no-warning -#endif -} -@end - - -// Don't warn about missing releases when -dealloc helpers are called. - -@interface ClassWithDeallocHelpers : NSObject -@property (retain) NSObject *ivarReleasedInMethod; -@property (retain) NSObject *propNilledOutInMethod; - -@property (retain) NSObject *ivarReleasedInFunction; -@property (retain) NSObject *propNilledOutInFunction; - -@property (retain) NSObject *ivarNeverReleased; -- (void)invalidateInMethod; -@end - -void ReleaseValueHelper(NSObject *iv) { -#if NON_ARC - [iv release]; -#endif -} - -void NilOutPropertyHelper(ClassWithDeallocHelpers *o) { - o.propNilledOutInFunction = nil; -} - -@implementation ClassWithDeallocHelpers -- (void)invalidateInMethod { -#if NON_ARC - [_ivarReleasedInMethod release]; -#endif - self.propNilledOutInMethod = nil; -} - -- (void)dealloc; { - ReleaseValueHelper(_ivarReleasedInFunction); - NilOutPropertyHelper(self); - - [self invalidateInMethod]; -#if NON_ARC - [super dealloc]; // expected-warning {{The '_ivarNeverReleased' ivar in 'ClassWithDeallocHelpers' was retained by a synthesized property but not released before '[super dealloc]'}} -#endif -} -@end - - -// Don't warn when self in -dealloc escapes. - -@interface ClassWhereSelfEscapesViaMethodCall : NSObject -@property (retain) NSObject *ivar; // no-warning -@end - -@interface ClassWhereSelfEscapesViaMethodCall (Other) -- (void)invalidate; // In other translation unit. -@end - -@implementation ClassWhereSelfEscapesViaMethodCall -- (void)dealloc; { - [self invalidate]; -#if NON_ARC - [super dealloc]; -#endif -} // no-warning -@end - -@interface ClassWhereSelfEscapesViaPropertyAccess : NSObject -@property (retain) NSObject *ivar; -@end - -@interface ClassWhereSelfEscapesViaPropertyAccess (Other) -// The implementation of this property is unknown and therefore could -// release ivar. -@property (retain) NSObject *otherIvar; -@end - -@implementation ClassWhereSelfEscapesViaPropertyAccess -- (void)dealloc; { - self.otherIvar = nil; #if NON_ARC [super dealloc]; #endif -} // no-warning -@end - -// Don't treat self as escaping when setter called on *synthesized* -// property. - -@interface ClassWhereSelfEscapesViaSynthesizedPropertyAccess : NSObject -@property (retain) NSObject *ivar; -@property (retain) NSObject *otherIvar; -@end - -@implementation ClassWhereSelfEscapesViaSynthesizedPropertyAccess -- (void)dealloc; { - self.otherIvar = nil; -#if NON_ARC - [super dealloc]; // expected-warning {{The '_ivar' ivar in 'ClassWhereSelfEscapesViaSynthesizedPropertyAccess' was retained by a synthesized property but not released before '[super dealloc]'}} -#endif } -@end - -// Don't warn when value escapes. -@interface ClassWhereIvarValueEscapes : NSObject -@property (retain) NSObject *ivar; -@end - -void ReleaseMe(id arg); - -@implementation ClassWhereIvarValueEscapes -- (void)dealloc; { - - ReleaseMe(_ivar); - -#if NON_ARC - [super dealloc]; -#endif -} // no-warning -@end - -// Don't warn when value is known to be nil. - -@interface ClassWhereIvarIsNil : NSObject -@property (retain) NSObject *ivarIsNil; -@end - -@implementation ClassWhereIvarIsNil -- (void)dealloc; { - -#if NON_ARC - if (_ivarIsNil) - [_ivarIsNil release]; - - [super dealloc]; -#endif -} // no-warning -@end - - -// Don't warn for non-retainable properties. - -@interface ClassWithNonRetainableProperty : NSObject -@property (assign) int *ivar; // no-warning -@end - -@implementation ClassWithNonRetainableProperty -- (void)dealloc; { -#if NON_ARC - [super dealloc]; -#endif -} // no-warning -@end - - -@interface SuperClassOfClassWithInlinedSuperDealloc : NSObject -@property (retain) NSObject *propInSuper; -@end - -@implementation SuperClassOfClassWithInlinedSuperDealloc -- (void)dealloc { -#if NON_ARC - [super dealloc]; // expected-warning {{The '_propInSuper' ivar in 'SuperClassOfClassWithInlinedSuperDealloc' was retained by a synthesized property but not released before '[super dealloc]'}} -#endif -} -@end - -@interface ClassWithInlinedSuperDealloc : SuperClassOfClassWithInlinedSuperDealloc -@property (retain) NSObject *propInSub; -@end - -@implementation ClassWithInlinedSuperDealloc -- (void)dealloc { -#if NON_ARC - [super dealloc]; // expected-warning {{The '_propInSub' ivar in 'ClassWithInlinedSuperDealloc' was retained by a synthesized property but not released before '[super dealloc]'}} -#endif -} -@end - - -@interface SuperClassOfClassWithInlinedSuperDeallocAndInvalidation : NSObject -@property (retain) NSObject *propInSuper; - -- (void)invalidate; -@end - -@implementation SuperClassOfClassWithInlinedSuperDeallocAndInvalidation - -- (void)invalidate { -#if NON_ARC - [_propInSuper release]; -#endif - _propInSuper = nil; -} - -- (void)dealloc { - [self invalidate]; -#if NON_ARC - [super dealloc]; // no-warning -#endif -} -@end - -@interface ClassWithInlinedSuperDeallocAndInvalidation : SuperClassOfClassWithInlinedSuperDeallocAndInvalidation -@property (retain) NSObject *propInSub; -@end - -@implementation ClassWithInlinedSuperDeallocAndInvalidation - -- (void)invalidate { -#if NON_ARC - [_propInSub release]; -#endif - [super invalidate]; -} - -- (void)dealloc { -#if NON_ARC - [super dealloc]; // no-warning -#endif -} -@end - - -@interface SuperClassOfClassThatEscapesBeforeInliningSuper : NSObject -@property (retain) NSObject *propInSuper; -@end - -@implementation SuperClassOfClassThatEscapesBeforeInliningSuper - -- (void)dealloc { - -#if NON_ARC - [super dealloc]; // expected-warning {{The '_propInSuper' ivar in 'SuperClassOfClassThatEscapesBeforeInliningSuper' was retained by a synthesized property but not released before '[super dealloc]'}} -#endif -} -@end - -@interface ClassThatEscapesBeforeInliningSuper : SuperClassOfClassThatEscapesBeforeInliningSuper -@property (retain) NSObject *propInSub; -@end - -@interface ClassThatEscapesBeforeInliningSuper (Other) -- (void)invalidate; // No implementation in translation unit. -@end - -@implementation ClassThatEscapesBeforeInliningSuper -- (void)dealloc { - [self invalidate]; - -#if NON_ARC - [super dealloc]; // no-warning -#endif -} -@end - - -#if NON_ARC -@interface ReleaseIvarInField : NSObject { - int _tag; - union { - NSObject *field1; - NSObject *field2; - } _someUnion; - - struct { - NSObject *field1; - } _someStruct; -} -@end - -@implementation ReleaseIvarInField -- (void)dealloc { - if (_tag) { - [_someUnion.field1 release]; - } else { - [_someUnion.field2 release]; - } - - [_someStruct.field1 release]; - [super dealloc]; -} -@end -#endif - -#if NON_ARC -@interface ReleaseIvarInArray : NSObject { - NSObject *_array[3]; -} -@end - -@implementation ReleaseIvarInArray -- (void)dealloc { - for (int i = 0; i < 3; i++) { - [_array[i] release]; - } - [super dealloc]; -} -@end -#endif - -// Don't warn about missing releases for subclasses of SenTestCase or -// for classes that are not subclasses of NSObject. - -@interface SenTestCase : NSObject {} -@end - -@interface MyClassTest : SenTestCase -@property (retain) NSObject *ivar; -@end - -@implementation MyClassTest --(void)tearDown { -#if NON_ARC - [_ivar release]; -#endif -} - --(void)dealloc; { -#if NON_ARC - [super dealloc]; // no-warning -#endif -} -@end - -__attribute__((objc_root_class)) -@interface NonNSObjectMissingDealloc -@property (retain) NSObject *ivar; -@end -@implementation NonNSObjectMissingDealloc --(void)dealloc; { - -} @end +// CHECK: 4 warnings generated. diff --git a/test/Analysis/MissingDealloc.m b/test/Analysis/MissingDealloc.m index ae880546ff9..d6af44b895b 100644 --- a/test/Analysis/MissingDealloc.m +++ b/test/Analysis/MissingDealloc.m @@ -128,9 +128,6 @@ @interface SenTestCase : NSObject {} @interface MyClassTest : SenTestCase { NSString *resourcePath; } - -@property (retain) NSObject *ivar; - @end @interface NSBundle : NSObject {} @@ -146,15 +143,4 @@ - (void)testXXX { // do something which uses resourcepath } @end - -//===------------------------------------------------------------------------=== -// Don't warn for clases that aren't subclasses of NSObject - -__attribute__((objc_root_class)) -@interface NonNSObjectMissingDealloc -@property (retain) NSObject *ivar; -@end -@implementation NonNSObjectMissingDealloc -@end - // CHECK: 4 warnings generated. diff --git a/test/Analysis/PR2978.m b/test/Analysis/PR2978.m index bb0fc414566..2067b3e85af 100644 --- a/test/Analysis/PR2978.m +++ b/test/Analysis/PR2978.m @@ -5,7 +5,7 @@ @interface NSObject - (void)release; -- (void)dealloc; +- dealloc; @end @interface MyClass : NSObject { @@ -19,14 +19,8 @@ @interface MyClass : NSObject { id _M; id _P; id _Q; - id _R; - id _S; id _V; id _W; - - MyClass *_other; - - id _nonPropertyIvar; } @property(retain) id X; @property(retain) id Y; @@ -35,13 +29,8 @@ @interface MyClass : NSObject { @property(weak) id L; @property(readonly) id N; @property(retain) id M; -@property(weak) id P; +@property(weak) id P; // expected-warning {{'_P' instance variable in 'MyClass' was not retained by a synthesized property but was released in 'dealloc'}} @property(weak) id Q; -@property(retain) id R; -@property(weak, readonly) id S; - -@property(assign, readonly) id T; // Shadowed in class extension -@property(assign) id U; @property(retain) id V; @property(retain) id W; @@ -49,67 +38,36 @@ -(id) O; -(void) setO: (id) arg; @end -@interface MyClass () -// Shadows T to make it readwrite internally but readonly externally. -@property(assign, readwrite) id T; -@end - @implementation MyClass @synthesize X = _X; -@synthesize Y = _Y; -@synthesize Z = _Z; +@synthesize Y = _Y; // expected-warning{{The '_Y' instance variable in 'MyClass' was retained by a synthesized property but was not released in 'dealloc'}} +@synthesize Z = _Z; // expected-warning{{The '_Z' instance variable in 'MyClass' was not retained by a synthesized property but was released in 'dealloc'}} @synthesize K = _K; -@synthesize L = _L; -@synthesize N = _N; +@synthesize L = _L; // no-warning +@synthesize N = _N; // no-warning @synthesize M = _M; -@synthesize Q = _Q; -@synthesize R = _R; +@synthesize Q = _Q; // expected-warning {{'_Q' instance variable in 'MyClass' was not retained by a synthesized property but was released in 'dealloc'}} @synthesize V = _V; -@synthesize W = _W; +@synthesize W = _W; // expected-warning{{The '_W' instance variable in 'MyClass' was retained by a synthesized property but was not released in 'dealloc'}} -(id) O{ return 0; } -(void) setO:(id)arg { } - --(void) releaseInHelper { - [_R release]; // no-warning - _R = @"Hi"; -} - -- (void)dealloc +- (id)dealloc { - [_X release]; - [_Z release]; // expected-warning{{The '_Z' ivar in 'MyClass' was synthesized for an assign, readwrite property but was released in 'dealloc'}} - [_T release]; // no-warning - - [_other->_Z release]; // no-warning + [_Z release]; [_N release]; - + self.M = 0; // This will release '_M' [self setV:0]; // This will release '_V' [self setW:@"newW"]; // This will release '_W', but retain the new value + self.O = 0; // no-warning - [_S release]; // expected-warning {{The '_S' ivar in 'MyClass' was synthesized for a weak property but was released in 'dealloc'}} - - self.O = 0; // no-warning - - [_Q release]; // expected-warning {{The '_Q' ivar in 'MyClass' was synthesized for a weak property but was released in 'dealloc'}} - + [_Q release]; self.P = 0; - - [self releaseInHelper]; - - [_nonPropertyIvar release]; // no-warning - - // Silly, but not an error. - if (!_U) - [_U release]; - [super dealloc]; - // expected-warning@-1{{The '_Y' ivar in 'MyClass' was retained by a synthesized property but not released before '[super dealloc]'}} - // expected-warning@-2{{The '_W' ivar in 'MyClass' was retained by a synthesized property but not released before '[super dealloc]'}} - + return 0; } @end diff --git a/test/Analysis/properties.m b/test/Analysis/properties.m index 98d6d7ce1d7..d92f1a1a7e8 100644 --- a/test/Analysis/properties.m +++ b/test/Analysis/properties.m @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount,alpha.osx.cocoa.Dealloc,debug.ExprInspection -analyzer-store=region -verify -Wno-objc-root-class %s -// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount,alpha.osx.cocoa.Dealloc,debug.ExprInspection -analyzer-store=region -verify -Wno-objc-root-class -fobjc-arc %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount,debug.ExprInspection -analyzer-store=region -verify -Wno-objc-root-class %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount,debug.ExprInspection -analyzer-store=region -verify -Wno-objc-root-class -fobjc-arc %s void clang_analyzer_eval(int); @@ -22,7 +22,6 @@ -(id)autorelease; -(id)copy; -(id)retain; -(oneway void)release; --(void)dealloc; @end @interface NSString : NSObject - (NSUInteger)length; @@ -139,14 +138,6 @@ @interface Person : NSObject { @implementation Person @synthesize name = _name; - --(void)dealloc { -#if !__has_feature(objc_arc) - self.name = [[NSString alloc] init]; // expected-warning {{leak}} - - [super dealloc]; // expected-warning {{The '_name' ivar in 'Person' was retained by a synthesized property but not released before '[super dealloc]}} -#endif -} @end #if !__has_feature(objc_arc) From 58d30aa71b6b4c1283bc5669a3f978618ec0c70a Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Thu, 25 Feb 2016 21:15:16 +0000 Subject: [PATCH 227/742] [analyzer] Reapply r261917 with a fix. This reapplies "[analyzer] Make ObjCDeallocChecker path sensitive." (r261917) with a fix for an error on some bots about specializing a template from another namespace. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261929 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../Checkers/CheckObjCDealloc.cpp | 927 ++++++++++++++---- test/Analysis/DeallocMissingRelease.m | 476 ++++++++- test/Analysis/MissingDealloc.m | 14 + test/Analysis/PR2978.m | 70 +- test/Analysis/properties.m | 13 +- 5 files changed, 1286 insertions(+), 214 deletions(-) diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index 902babfe502..28fae7ca1b3 100644 --- a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -7,9 +7,24 @@ // //===----------------------------------------------------------------------===// // -// This file defines a CheckObjCDealloc, a checker that -// analyzes an Objective-C class's implementation to determine if it -// correctly implements -dealloc. +// This checker analyzes Objective-C -dealloc methods and their callees +// to warn about improper releasing of instance variables that back synthesized +// properties. It warns about missing releases in the following cases: +// - When a class has a synthesized instance variable for a 'retain' or 'copy' +// property and lacks a -dealloc method in its implementation. +// - When a class has a synthesized instance variable for a 'retain'/'copy' +// property but the ivar is not released in -dealloc by either -release +// or by nilling out the property. +// +// It warns about extra releases in -dealloc (but not in callees) when a +// synthesized instance variable is released in the following cases: +// - When the property is 'assign' and is not 'readonly'. +// - When the property is 'weak'. +// +// This checker only warns for instance variables synthesized to back +// properties. Handling the more general case would require inferring whether +// an instance variable is stored retained or not. For synthesized properties, +// this is specified in the property declaration itself. // //===----------------------------------------------------------------------===// @@ -20,71 +35,36 @@ #include "clang/AST/ExprObjC.h" #include "clang/Basic/LangOptions.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" #include "llvm/Support/raw_ostream.h" using namespace clang; using namespace ento; -// FIXME: This was taken from IvarInvalidationChecker.cpp -static const Expr *peel(const Expr *E) { - E = E->IgnoreParenCasts(); - if (const PseudoObjectExpr *POE = dyn_cast(E)) - E = POE->getSyntacticForm()->IgnoreParenCasts(); - if (const OpaqueValueExpr *OVE = dyn_cast(E)) - E = OVE->getSourceExpr()->IgnoreParenCasts(); - return E; -} +/// Indicates whether an instance variable is required to be released in +/// -dealloc. +enum class ReleaseRequirement { + /// The instance variable must be released, either by calling + /// -release on it directly or by nilling it out with a property setter. + MustRelease, -static bool scan_ivar_release(Stmt *S, const ObjCIvarDecl *ID, - const ObjCPropertyDecl *PD, - Selector Release, - IdentifierInfo* SelfII, - ASTContext &Ctx) { - - // [mMyIvar release] - if (ObjCMessageExpr *ME = dyn_cast(S)) - if (ME->getSelector() == Release) - if (ME->getInstanceReceiver()) - if (const Expr *Receiver = peel(ME->getInstanceReceiver())) - if (auto *E = dyn_cast(Receiver)) - if (E->getDecl() == ID) - return true; - - // [self setMyIvar:nil]; - if (ObjCMessageExpr *ME = dyn_cast(S)) - if (ME->getInstanceReceiver()) - if (const Expr *Receiver = peel(ME->getInstanceReceiver())) - if (auto *E = dyn_cast(Receiver)) - if (E->getDecl()->getIdentifier() == SelfII) - if (ME->getMethodDecl() == PD->getSetterMethodDecl() && - ME->getNumArgs() == 1 && - peel(ME->getArg(0))->isNullPointerConstant(Ctx, - Expr::NPC_ValueDependentIsNull)) - return true; - - // self.myIvar = nil; - if (BinaryOperator* BO = dyn_cast(S)) - if (BO->isAssignmentOp()) - if (auto *PRE = dyn_cast(peel(BO->getLHS()))) - if (PRE->isExplicitProperty() && PRE->getExplicitProperty() == PD) - if (peel(BO->getRHS())->isNullPointerConstant(Ctx, - Expr::NPC_ValueDependentIsNull)) { - // This is only a 'release' if the property kind is not - // 'assign'. - return PD->getSetterKind() != ObjCPropertyDecl::Assign; - } - - // Recurse to children. - for (Stmt *SubStmt : S->children()) - if (SubStmt && scan_ivar_release(SubStmt, ID, PD, Release, SelfII, Ctx)) - return true; + /// The instance variable must not be directly released with -release. + MustNotReleaseDirectly, - return false; -} + /// The requirement for the instance variable could not be determined. + Unknown +}; +/// Returns true if the property implementation is synthesized and the +/// type of the property is retainable. static bool isSynthesizedRetainableProperty(const ObjCPropertyImplDecl *I, const ObjCIvarDecl **ID, const ObjCPropertyDecl **PD) { @@ -107,33 +87,107 @@ static bool isSynthesizedRetainableProperty(const ObjCPropertyImplDecl *I, return true; } -static bool synthesizedPropertyRequiresRelease(const ObjCPropertyDecl *PD) { - // A synthesized property must be released if and only if the kind of setter - // was neither 'assign' or 'weak'. - ObjCPropertyDecl::SetterKind SK = PD->getSetterKind(); - return (SK != ObjCPropertyDecl::Assign && SK != ObjCPropertyDecl::Weak); -} +namespace { + +class ObjCDeallocChecker + : public Checker, + check::PreObjCMessage, check::PostObjCMessage, + check::BeginFunction, check::EndFunction, + check::PointerEscape, + check::PreStmt> { + + mutable IdentifierInfo *NSObjectII, *SenTestCaseII; + mutable Selector DeallocSel, ReleaseSel; + + std::unique_ptr MissingReleaseBugType; + std::unique_ptr ExtraReleaseBugType; + +public: + ObjCDeallocChecker(); + + void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr, + BugReporter &BR) const; + void checkBeginFunction(CheckerContext &Ctx) const; + void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; + void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; + + ProgramStateRef checkPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind) const; + void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const; + void checkEndFunction(CheckerContext &Ctx) const; + +private: + void diagnoseMissingReleases(CheckerContext &C) const; + + bool diagnoseExtraRelease(SymbolRef ReleasedValue, const ObjCMethodCall &M, + CheckerContext &C) const; + + SymbolRef getValueExplicitlyReleased(const ObjCMethodCall &M, + CheckerContext &C) const; + SymbolRef getValueReleasedByNillingOut(const ObjCMethodCall &M, + CheckerContext &C) const; + + SymbolRef getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym) const; + + ReleaseRequirement + getDeallocReleaseRequirement(const ObjCPropertyImplDecl *PropImpl) const; + + bool isInInstanceDealloc(const CheckerContext &C, SVal &SelfValOut) const; + bool isInInstanceDealloc(const CheckerContext &C, const LocationContext *LCtx, + SVal &SelfValOut) const; + bool instanceDeallocIsOnStack(const CheckerContext &C, + SVal &InstanceValOut) const; + + bool isSuperDeallocMessage(const ObjCMethodCall &M) const; + + const ObjCImplDecl *getContainingObjCImpl(const LocationContext *LCtx) const; + + const ObjCPropertyDecl * + findShadowedPropertyDecl(const ObjCPropertyImplDecl *PropImpl) const; + + ProgramStateRef removeValueRequiringRelease(ProgramStateRef State, + SymbolRef InstanceSym, + SymbolRef ValueSym) const; + + void initIdentifierInfoAndSelectors(ASTContext &Ctx) const; + + bool classHasSeparateTeardown(const ObjCInterfaceDecl *ID) const; +}; +} // End anonymous namespace. + +typedef llvm::ImmutableSet SymbolSet; + +/// Maps from the symbol for a class instance to the set of +/// symbols remaining that must be released in -dealloc. +REGISTER_MAP_WITH_PROGRAMSTATE(UnreleasedIvarMap, SymbolRef, SymbolSet) -static void checkObjCDealloc(const CheckerBase *Checker, - const ObjCImplementationDecl *D, - const LangOptions &LOpts, BugReporter &BR) { +namespace clang { +namespace ento { +template<> struct ProgramStateTrait +: public ProgramStatePartialTrait { + static void *GDMIndex() { static int index = 0; return &index; } +}; +} +} - assert(LOpts.getGC() != LangOptions::GCOnly); - assert(!LOpts.ObjCAutoRefCount); +/// An AST check that diagnose when the class requires a -dealloc method and +/// is missing one. +void ObjCDeallocChecker::checkASTDecl(const ObjCImplementationDecl *D, + AnalysisManager &Mgr, + BugReporter &BR) const { + assert(Mgr.getLangOpts().getGC() != LangOptions::GCOnly); + assert(!Mgr.getLangOpts().ObjCAutoRefCount); + initIdentifierInfoAndSelectors(Mgr.getASTContext()); - ASTContext &Ctx = BR.getContext(); const ObjCInterfaceDecl *ID = D->getClassInterface(); // Does the class contain any synthesized properties that are retainable? // If not, skip the check entirely. bool containsRetainedSynthesizedProperty = false; for (const auto *I : D->property_impls()) { - const ObjCIvarDecl *ID = nullptr; - const ObjCPropertyDecl *PD = nullptr; - if (!isSynthesizedRetainableProperty(I, &ID, &PD)) - continue; - - if (synthesizedPropertyRequiresRelease(PD)) { + if (getDeallocReleaseRequirement(I) == ReleaseRequirement::MustRelease) { containsRetainedSynthesizedProperty = true; break; } @@ -142,136 +196,659 @@ static void checkObjCDealloc(const CheckerBase *Checker, if (!containsRetainedSynthesizedProperty) return; - // Determine if the class subclasses NSObject. - IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject"); - IdentifierInfo* SenTestCaseII = &Ctx.Idents.get("SenTestCase"); - - for ( ; ID ; ID = ID->getSuperClass()) { - IdentifierInfo *II = ID->getIdentifier(); - - if (II == NSObjectII) - break; - - // FIXME: For now, ignore classes that subclass SenTestCase, as these don't - // need to implement -dealloc. They implement tear down in another way, - // which we should try and catch later. - // http://llvm.org/bugs/show_bug.cgi?id=3187 - if (II == SenTestCaseII) - return; - } - - if (!ID) + // If the class is known to have a lifecycle with a separate teardown method + // then it may not require a -dealloc method. + if (classHasSeparateTeardown(ID)) return; - // Get the "dealloc" selector. - IdentifierInfo* II = &Ctx.Idents.get("dealloc"); - Selector S = Ctx.Selectors.getSelector(0, &II); const ObjCMethodDecl *MD = nullptr; // Scan the instance methods for "dealloc". for (const auto *I : D->instance_methods()) { - if (I->getSelector() == S) { + if (I->getSelector() == DeallocSel) { MD = I; break; } } if (!MD) { // No dealloc found. + const char* Name = "Missing -dealloc"; - const char* name = LOpts.getGC() == LangOptions::NonGC - ? "missing -dealloc" - : "missing -dealloc (Hybrid MM, non-GC)"; - - std::string buf; - llvm::raw_string_ostream os(buf); - os << "Objective-C class '" << *D << "' lacks a 'dealloc' instance method"; + std::string Buf; + llvm::raw_string_ostream OS(Buf); + OS << "Objective-C class '" << *D << "' lacks a 'dealloc' instance method"; PathDiagnosticLocation DLoc = PathDiagnosticLocation::createBegin(D, BR.getSourceManager()); - BR.EmitBasicReport(D, Checker, name, categories::CoreFoundationObjectiveC, - os.str(), DLoc); + BR.EmitBasicReport(D, this, Name, categories::CoreFoundationObjectiveC, + OS.str(), DLoc); return; } +} - // Get the "release" selector. - IdentifierInfo* RII = &Ctx.Idents.get("release"); - Selector RS = Ctx.Selectors.getSelector(0, &RII); +/// If this is the beginning of -dealloc, mark the values initially stored in +/// instance variables that must be released by the end of -dealloc +/// as unreleased in the state. +void ObjCDeallocChecker::checkBeginFunction( + CheckerContext &C) const { + initIdentifierInfoAndSelectors(C.getASTContext()); - // Get the "self" identifier - IdentifierInfo* SelfII = &Ctx.Idents.get("self"); + // Only do this if the current method is -dealloc. + SVal SelfVal; + if (!isInInstanceDealloc(C, SelfVal)) + return; - // Scan for missing and extra releases of ivars used by implementations - // of synthesized properties - for (const auto *I : D->property_impls()) { - const ObjCIvarDecl *ID = nullptr; - const ObjCPropertyDecl *PD = nullptr; - if (!isSynthesizedRetainableProperty(I, &ID, &PD)) + SymbolRef SelfSymbol = SelfVal.getAsSymbol(); + + const LocationContext *LCtx = C.getLocationContext(); + ProgramStateRef InitialState = C.getState(); + + ProgramStateRef State = InitialState; + + SymbolSet::Factory &F = State->getStateManager().get_context(); + + // Symbols that must be released by the end of the -dealloc; + SymbolSet RequiredReleases = F.getEmptySet(); + + // If we're an inlined -dealloc, we should add our symbols to the existing + // set from our subclass. + if (const SymbolSet *CurrSet = State->get(SelfSymbol)) + RequiredReleases = *CurrSet; + + for (auto *PropImpl : getContainingObjCImpl(LCtx)->property_impls()) { + ReleaseRequirement Requirement = getDeallocReleaseRequirement(PropImpl); + if (Requirement != ReleaseRequirement::MustRelease) continue; - bool requiresRelease = synthesizedPropertyRequiresRelease(PD); - if (scan_ivar_release(MD->getBody(), ID, PD, RS, SelfII, Ctx) - != requiresRelease) { - const char *name = nullptr; - std::string buf; - llvm::raw_string_ostream os(buf); - - if (requiresRelease) { - name = LOpts.getGC() == LangOptions::NonGC - ? "missing ivar release (leak)" - : "missing ivar release (Hybrid MM, non-GC)"; - - os << "The '" << *ID << "' instance variable in '" << *D - << "' was retained by a synthesized property " - "but was not released in 'dealloc'"; - } else { - // It is common for the ivars for read-only assign properties to - // always be stored retained, so don't warn for a release in - // dealloc for the ivar backing these properties. - if (PD->isReadOnly()) - continue; - - name = LOpts.getGC() == LangOptions::NonGC - ? "extra ivar release (use-after-release)" - : "extra ivar release (Hybrid MM, non-GC)"; - - os << "The '" << *ID << "' instance variable in '" << *D - << "' was not retained by a synthesized property " - "but was released in 'dealloc'"; - } - - // If @synthesize statement is missing, fall back to @property statement. - const Decl *SPDecl = I->getLocation().isValid() - ? static_cast(I) - : static_cast(PD); - PathDiagnosticLocation SPLoc = - PathDiagnosticLocation::createBegin(SPDecl, BR.getSourceManager()); - - BR.EmitBasicReport(MD, Checker, name, - categories::CoreFoundationObjectiveC, os.str(), SPLoc); - } + SVal LVal = State->getLValue(PropImpl->getPropertyIvarDecl(), SelfVal); + Optional LValLoc = LVal.getAs(); + if (!LValLoc) + continue; + + SVal InitialVal = State->getSVal(LValLoc.getValue()); + SymbolRef Symbol = InitialVal.getAsSymbol(); + if (!Symbol || !isa(Symbol)) + continue; + + // Mark the value as requiring a release. + RequiredReleases = F.add(RequiredReleases, Symbol); + } + + if (!RequiredReleases.isEmpty()) { + State = State->set(SelfSymbol, RequiredReleases); + } + + if (State != InitialState) { + C.addTransition(State); } } -//===----------------------------------------------------------------------===// -// ObjCDeallocChecker -//===----------------------------------------------------------------------===// +/// Given a symbol for an ivar, return a symbol for the instance containing +/// the ivar. Returns nullptr if the instance symbol cannot be found. +SymbolRef +ObjCDeallocChecker::getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym) const { + if (auto *SRV = dyn_cast(IvarSym)) { + const TypedValueRegion *TVR = SRV->getRegion(); + const ObjCIvarRegion *IvarRegion = dyn_cast_or_null(TVR); + if (!IvarRegion) + return nullptr; + + return IvarRegion->getSymbolicBase()->getSymbol(); + } -namespace { -class ObjCDeallocChecker : public Checker< - check::ASTDecl > { -public: - void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr, - BugReporter &BR) const { - if (mgr.getLangOpts().getGC() == LangOptions::GCOnly || - mgr.getLangOpts().ObjCAutoRefCount) + return nullptr; +} + +/// If we are in -dealloc or -dealloc is on the stack, handle the call if it is +/// a release or a nilling-out property setter. +void ObjCDeallocChecker::checkPreObjCMessage( + const ObjCMethodCall &M, CheckerContext &C) const { + // Only run if -dealloc is on the stack. + SVal DeallocedInstance; + if (!instanceDeallocIsOnStack(C, DeallocedInstance)) + return; + + SymbolRef ReleasedValue = getValueExplicitlyReleased(M, C); + + if (ReleasedValue) { + // An instance variable symbol was released with -release: + // [_property release]; + if (diagnoseExtraRelease(ReleasedValue,M, C)) return; - checkObjCDealloc(this, cast(D), mgr.getLangOpts(), - BR); + } else { + // An instance variable symbol was released nilling out its property: + // self.property = nil; + ReleasedValue = getValueReleasedByNillingOut(M, C); } -}; + + if (!ReleasedValue) + return; + + SymbolRef InstanceSym = getInstanceSymbolFromIvarSymbol(ReleasedValue); + if (!InstanceSym) + return; + ProgramStateRef InitialState = C.getState(); + + ProgramStateRef ReleasedState = + removeValueRequiringRelease(InitialState, InstanceSym, ReleasedValue); + + if (ReleasedState != InitialState) { + C.addTransition(ReleasedState); + } +} + +/// If the message was a call to '[super dealloc]', diagnose any missing +/// releases. +void ObjCDeallocChecker::checkPostObjCMessage( + const ObjCMethodCall &M, CheckerContext &C) const { + // We perform this check post-message so that if the super -dealloc + // calls a helper method and that this class overrides, any ivars released in + // the helper method will be recorded before checking. + if (isSuperDeallocMessage(M)) + diagnoseMissingReleases(C); +} + +/// Check for missing releases even when -dealloc does not call +/// '[super dealloc]'. +void ObjCDeallocChecker::checkEndFunction( + CheckerContext &C) const { + diagnoseMissingReleases(C); +} + +/// Check for missing releases on early return. +void ObjCDeallocChecker::checkPreStmt( + const ReturnStmt *RS, CheckerContext &C) const { + diagnoseMissingReleases(C); +} + +/// If a symbol escapes conservatively assume unseen code released it. +ProgramStateRef ObjCDeallocChecker::checkPointerEscape( + ProgramStateRef State, const InvalidatedSymbols &Escaped, + const CallEvent *Call, PointerEscapeKind Kind) const { + + // Don't treat calls to '[super dealloc]' as escaping for the purposes + // of this checker. Because the checker diagnoses missing releases in the + // post-message handler for '[super dealloc], escaping here would cause + // the checker to never warn. + auto *OMC = dyn_cast_or_null(Call); + if (OMC && isSuperDeallocMessage(*OMC)) + return State; + + for (const auto &Sym : Escaped) { + State = State->remove(Sym); + + SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(Sym); + if (!InstanceSymbol) + continue; + + State = removeValueRequiringRelease(State, InstanceSymbol, Sym); + } + + return State; +} + +/// Report any unreleased instance variables for the current instance being +/// dealloced. +void ObjCDeallocChecker::diagnoseMissingReleases(CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + SVal SelfVal; + if (!isInInstanceDealloc(C, SelfVal)) + return; + + const MemRegion *SelfRegion = SelfVal.castAs().getRegion(); + const LocationContext *LCtx = C.getLocationContext(); + + ExplodedNode *ErrNode = nullptr; + + SymbolRef SelfSym = SelfVal.getAsSymbol(); + if (!SelfSym) + return; + + const SymbolSet *OldUnreleased = State->get(SelfSym); + if (!OldUnreleased) + return; + + SymbolSet NewUnreleased = *OldUnreleased; + SymbolSet::Factory &F = State->getStateManager().get_context(); + + ProgramStateRef InitialState = State; + + for (auto *IvarSymbol : *OldUnreleased) { + const TypedValueRegion *TVR = + cast(IvarSymbol)->getRegion(); + const ObjCIvarRegion *IvarRegion = cast(TVR); + + // Don't warn if the ivar is not for this instance. + if (SelfRegion != IvarRegion->getSuperRegion()) + continue; + + // Prevent an inlined call to -dealloc in a super class from warning + // about the values the subclass's -dealloc should release. + if (IvarRegion->getDecl()->getContainingInterface() != + cast(LCtx->getDecl())->getClassInterface()) + continue; + + // Prevents diagnosing multiple times for the same instance variable + // at, for example, both a return and at the end of of the function. + NewUnreleased = F.remove(NewUnreleased, IvarSymbol); + + if (State->getStateManager() + .getConstraintManager() + .isNull(State, IvarSymbol) + .isConstrainedTrue()) { + continue; + } + + // A missing release manifests as a leak, so treat as a non-fatal error. + if (!ErrNode) + ErrNode = C.generateNonFatalErrorNode(); + // If we've already reached this node on another path, return without + // diagnosing. + if (!ErrNode) + return; + + std::string Buf; + llvm::raw_string_ostream OS(Buf); + + const ObjCIvarDecl *IvarDecl = IvarRegion->getDecl(); + const ObjCInterfaceDecl *Interface = IvarDecl->getContainingInterface(); + // If the class is known to have a lifecycle with teardown that is + // separate from -dealloc, do not warn about missing releases. We + // suppress here (rather than not tracking for instance variables in + // such classes) because these classes are rare. + if (classHasSeparateTeardown(Interface)) + return; + + ObjCImplDecl *ImplDecl = Interface->getImplementation(); + + const ObjCPropertyImplDecl *PropImpl = + ImplDecl->FindPropertyImplIvarDecl(IvarDecl->getIdentifier()); + + const ObjCPropertyDecl *PropDecl = PropImpl->getPropertyDecl(); + + assert(PropDecl->getSetterKind() == ObjCPropertyDecl::Copy || + PropDecl->getSetterKind() == ObjCPropertyDecl::Retain); + + OS << "The '" << *IvarDecl << "' ivar in '" << *ImplDecl + << "' was "; + + if (PropDecl->getSetterKind() == ObjCPropertyDecl::Retain) + OS << "retained"; + else + OS << "copied"; + + OS << " by a synthesized property but not released" + " before '[super dealloc]'"; + + std::unique_ptr BR( + new BugReport(*MissingReleaseBugType, OS.str(), ErrNode)); + + C.emitReport(std::move(BR)); + } + + if (NewUnreleased.isEmpty()) { + State = State->remove(SelfSym); + } else { + State = State->set(SelfSym, NewUnreleased); + } + + if (ErrNode) { + C.addTransition(State, ErrNode); + } else if (State != InitialState) { + C.addTransition(State); + } + + // Make sure that after checking in the top-most frame the list of + // tracked ivars is empty. This is intended to detect accidental leaks in + // the UnreleasedIvarMap program state. + assert(!LCtx->inTopFrame() || State->get().isEmpty()); +} + +/// Emits a warning if the current context is -dealloc and \param ReleasedValue +/// must not be directly released in a -dealloc. Returns true if a diagnostic +/// was emitted. +bool ObjCDeallocChecker::diagnoseExtraRelease(SymbolRef ReleasedValue, + const ObjCMethodCall &M, + CheckerContext &C) const { + SVal DeallocedInstance; + if (!isInInstanceDealloc(C, DeallocedInstance)) + return false; + + // Try to get the region from which the the released value was loaded. + // Note that, unlike diagnosing for missing releases, here we don't track + // values that must not be released in the state. This is because even if + // these values escape, it is still an error under the rules of MRR to + // release them in -dealloc. + const MemRegion *RegionLoadedFrom = nullptr; + if (auto *DerivedSym = dyn_cast(ReleasedValue)) + RegionLoadedFrom = DerivedSym->getRegion(); + else if (auto *RegionSym = dyn_cast(ReleasedValue)) + RegionLoadedFrom = RegionSym->getRegion(); + else + return false; + + auto *ReleasedIvar = dyn_cast(RegionLoadedFrom); + if (!ReleasedIvar) + return false; + + if (DeallocedInstance.castAs().getRegion() != + ReleasedIvar->getSuperRegion()) + return false; + + const LocationContext *LCtx = C.getLocationContext(); + const ObjCIvarDecl *ReleasedIvarDecl = ReleasedIvar->getDecl(); + + // If the ivar belongs to a property that must not be released directly + // in dealloc, emit a warning. + const ObjCImplDecl *Container = getContainingObjCImpl(LCtx); + const ObjCPropertyImplDecl *PropImpl = + Container->FindPropertyImplIvarDecl(ReleasedIvarDecl->getIdentifier()); + if (!PropImpl) + return false; + + if (getDeallocReleaseRequirement(PropImpl) != + ReleaseRequirement::MustNotReleaseDirectly) { + return false; + } + + // If the property is readwrite but it shadows a read-only property in its + // external interface, treat the property a read-only. If the outside + // world cannot write to a property then the internal implementation is free + // to make its own convention about whether the value is stored retained + // or not. We look up the shadow here rather than in + // getDeallocReleaseRequirement() because doing so can be expensive. + const ObjCPropertyDecl *PropDecl = findShadowedPropertyDecl(PropImpl); + if (PropDecl) { + if (PropDecl->isReadOnly()) + return false; + } else { + PropDecl = PropImpl->getPropertyDecl(); + } + + ExplodedNode *ErrNode = C.generateNonFatalErrorNode(); + if (!ErrNode) + return false; + + std::string Buf; + llvm::raw_string_ostream OS(Buf); + + assert(PropDecl->getSetterKind() == ObjCPropertyDecl::Weak || + (PropDecl->getSetterKind() == ObjCPropertyDecl::Assign && + !PropDecl->isReadOnly())); + + OS << "The '" << *PropImpl->getPropertyIvarDecl() + << "' ivar in '" << *Container + << "' was synthesized for "; + + if (PropDecl->getSetterKind() == ObjCPropertyDecl::Weak) + OS << "a weak"; + else + OS << "an assign, readwrite"; + + OS << " property but was released in 'dealloc'"; + + std::unique_ptr BR( + new BugReport(*ExtraReleaseBugType, OS.str(), ErrNode)); + BR->addRange(M.getOriginExpr()->getSourceRange()); + + C.emitReport(std::move(BR)); + + return true; +} + + +ObjCDeallocChecker:: + ObjCDeallocChecker() + : NSObjectII(nullptr), SenTestCaseII(nullptr) { + + MissingReleaseBugType.reset( + new BugType(this, "Missing ivar release (leak)", + categories::MemoryCoreFoundationObjectiveC)); + + ExtraReleaseBugType.reset( + new BugType(this, "Extra ivar release", + categories::MemoryCoreFoundationObjectiveC)); +} + +void ObjCDeallocChecker::initIdentifierInfoAndSelectors( + ASTContext &Ctx) const { + if (NSObjectII) + return; + + NSObjectII = &Ctx.Idents.get("NSObject"); + SenTestCaseII = &Ctx.Idents.get("SenTestCase"); + + IdentifierInfo *DeallocII = &Ctx.Idents.get("dealloc"); + IdentifierInfo *ReleaseII = &Ctx.Idents.get("release"); + DeallocSel = Ctx.Selectors.getSelector(0, &DeallocII); + ReleaseSel = Ctx.Selectors.getSelector(0, &ReleaseII); +} + +/// Returns true if \param M is a call to '[super dealloc]'. +bool ObjCDeallocChecker::isSuperDeallocMessage( + const ObjCMethodCall &M) const { + if (M.getOriginExpr()->getReceiverKind() != ObjCMessageExpr::SuperInstance) + return false; + + return M.getSelector() == DeallocSel; } -void ento::registerObjCDeallocChecker(CheckerManager &mgr) { - mgr.registerChecker(); +/// Returns the ObjCImplDecl containing the method declaration in \param LCtx. +const ObjCImplDecl * +ObjCDeallocChecker::getContainingObjCImpl(const LocationContext *LCtx) const { + auto *MD = cast(LCtx->getDecl()); + return cast(MD->getDeclContext()); +} + +/// Returns the property that shadowed by \param PropImpl if one exists and +/// nullptr otherwise. +const ObjCPropertyDecl *ObjCDeallocChecker::findShadowedPropertyDecl( + const ObjCPropertyImplDecl *PropImpl) const { + const ObjCPropertyDecl *PropDecl = PropImpl->getPropertyDecl(); + + // Only readwrite properties can shadow. + if (PropDecl->isReadOnly()) + return nullptr; + + auto *CatDecl = dyn_cast(PropDecl->getDeclContext()); + + // Only class extensions can contain shadowing properties. + if (!CatDecl || !CatDecl->IsClassExtension()) + return nullptr; + + IdentifierInfo *ID = PropDecl->getIdentifier(); + DeclContext::lookup_result R = CatDecl->getClassInterface()->lookup(ID); + for (DeclContext::lookup_iterator I = R.begin(), E = R.end(); I != E; ++I) { + auto *ShadowedPropDecl = dyn_cast(*I); + if (!ShadowedPropDecl) + continue; + + if (ShadowedPropDecl->isInstanceProperty()) { + assert(ShadowedPropDecl->isReadOnly()); + return ShadowedPropDecl; + } + } + + return nullptr; +} + +/// Remove the \param Value requiring a release from the tracked set for +/// \param Instance and return the resultant state. +ProgramStateRef ObjCDeallocChecker::removeValueRequiringRelease( + ProgramStateRef State, SymbolRef Instance, SymbolRef Value) const { + assert(Instance); + assert(Value); + + const SymbolSet *Unreleased = State->get(Instance); + if (!Unreleased) + return State; + + // Mark the value as no longer requiring a release. + SymbolSet::Factory &F = State->getStateManager().get_context(); + SymbolSet NewUnreleased = F.remove(*Unreleased, Value); + + if (NewUnreleased.isEmpty()) { + return State->remove(Instance); + } + + return State->set(Instance, NewUnreleased); +} + +/// Determines whether the instance variable for \p PropImpl must or must not be +/// released in -dealloc or whether it cannot be determined. +ReleaseRequirement ObjCDeallocChecker::getDeallocReleaseRequirement( + const ObjCPropertyImplDecl *PropImpl) const { + const ObjCIvarDecl *IvarDecl; + const ObjCPropertyDecl *PropDecl; + if (!isSynthesizedRetainableProperty(PropImpl, &IvarDecl, &PropDecl)) + return ReleaseRequirement::Unknown; + + ObjCPropertyDecl::SetterKind SK = PropDecl->getSetterKind(); + + switch (SK) { + // Retain and copy setters retain/copy their values before storing and so + // the value in their instance variables must be released in -dealloc. + case ObjCPropertyDecl::Retain: + case ObjCPropertyDecl::Copy: + return ReleaseRequirement::MustRelease; + + case ObjCPropertyDecl::Weak: + return ReleaseRequirement::MustNotReleaseDirectly; + + case ObjCPropertyDecl::Assign: + // It is common for the ivars for read-only assign properties to + // always be stored retained, so their release requirement cannot be + // be determined. + if (PropDecl->isReadOnly()) + return ReleaseRequirement::Unknown; + + return ReleaseRequirement::MustNotReleaseDirectly; + } + llvm_unreachable("Unrecognized setter kind"); +} + +/// Returns the released value if \param M is a call to -release. Returns +/// nullptr otherwise. +SymbolRef +ObjCDeallocChecker::getValueExplicitlyReleased(const ObjCMethodCall &M, + CheckerContext &C) const { + if (M.getSelector() != ReleaseSel) + return nullptr; + + return M.getReceiverSVal().getAsSymbol(); +} + +/// Returns the released value if \param M is a call a setter that releases +/// and nils out its underlying instance variable. +SymbolRef +ObjCDeallocChecker::getValueReleasedByNillingOut(const ObjCMethodCall &M, + CheckerContext &C) const { + SVal ReceiverVal = M.getReceiverSVal(); + if (!ReceiverVal.isValid()) + return nullptr; + + // Is the first argument nil? + if (M.getNumArgs() == 0) + return nullptr; + SVal Arg = M.getArgSVal(0); + ProgramStateRef notNilState, nilState; + std::tie(notNilState, nilState) = + M.getState()->assume(Arg.castAs()); + if (!(nilState && !notNilState)) + return nullptr; + + const ObjCPropertyDecl *Prop = M.getAccessedProperty(); + if (!Prop) + return nullptr; + + ObjCIvarDecl *PropIvarDecl = Prop->getPropertyIvarDecl(); + if (!PropIvarDecl) + return nullptr; + + ProgramStateRef State = C.getState(); + + SVal LVal = State->getLValue(PropIvarDecl, ReceiverVal); + Optional LValLoc = LVal.getAs(); + if (!LValLoc) + return nullptr; + + SVal CurrentValInIvar = State->getSVal(LValLoc.getValue()); + return CurrentValInIvar.getAsSymbol(); +} + +/// Returns true if the current context is a call to -dealloc and false +/// otherwise. If true, it also sets \param SelfValOut to the value of +/// 'self'. +bool ObjCDeallocChecker::isInInstanceDealloc(const CheckerContext &C, + SVal &SelfValOut) const { + return isInInstanceDealloc(C, C.getLocationContext(), SelfValOut); +} + +/// Returns true if \param LCtx is a call to -dealloc and false +/// otherwise. If true, it also sets \param SelfValOut to the value of +/// 'self'. +bool ObjCDeallocChecker::isInInstanceDealloc(const CheckerContext &C, + const LocationContext *LCtx, + SVal &SelfValOut) const { + auto *MD = dyn_cast(LCtx->getDecl()); + if (!MD || !MD->isInstanceMethod() || MD->getSelector() != DeallocSel) + return false; + + const ImplicitParamDecl *SelfDecl = LCtx->getSelfDecl(); + assert(SelfDecl && "No self in -dealloc?"); + + ProgramStateRef State = C.getState(); + SelfValOut = State->getSVal(State->getRegion(SelfDecl, LCtx)); + return true; +} + +/// Returns true if there is a call to -dealloc anywhere on the stack and false +/// otherwise. If true, it also sets \param InstanceValOut to the value of +/// 'self' in the frame for -dealloc. +bool ObjCDeallocChecker::instanceDeallocIsOnStack(const CheckerContext &C, + SVal &InstanceValOut) const { + const LocationContext *LCtx = C.getLocationContext(); + + while (LCtx) { + if (isInInstanceDealloc(C, LCtx, InstanceValOut)) + return true; + + LCtx = LCtx->getParent(); + } + + return false; +} + +/// Returns true if the \param ID is a class in which which is known to have +/// a separate teardown lifecycle. In this case, -dealloc warnings +/// about missing releases should be suppressed. +bool ObjCDeallocChecker::classHasSeparateTeardown( + const ObjCInterfaceDecl *ID) const { + // Suppress if the class is not a subclass of NSObject. + for ( ; ID ; ID = ID->getSuperClass()) { + IdentifierInfo *II = ID->getIdentifier(); + + if (II == NSObjectII) + return false; + + // FIXME: For now, ignore classes that subclass SenTestCase, as these don't + // need to implement -dealloc. They implement tear down in another way, + // which we should try and catch later. + // http://llvm.org/bugs/show_bug.cgi?id=3187 + if (II == SenTestCaseII) + return true; + } + + return true; +} + +void ento::registerObjCDeallocChecker(CheckerManager &Mgr) { + const LangOptions &LangOpts = Mgr.getLangOpts(); + // These checker only makes sense under MRR. + if (LangOpts.getGC() == LangOptions::GCOnly || LangOpts.ObjCAutoRefCount) + return; + + Mgr.registerChecker(); } diff --git a/test/Analysis/DeallocMissingRelease.m b/test/Analysis/DeallocMissingRelease.m index e782f9968f9..c7769db3d5f 100644 --- a/test/Analysis/DeallocMissingRelease.m +++ b/test/Analysis/DeallocMissingRelease.m @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.osx.cocoa.Dealloc -fblocks %s 2>&1 | FileCheck -check-prefix=CHECK %s -// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.osx.cocoa.Dealloc -fblocks -triple x86_64-apple-darwin10 -fobjc-arc -fobjc-runtime-has-weak %s 2>&1 | FileCheck -check-prefix=CHECK-ARC -allow-empty '--implicit-check-not=error:' '--implicit-check-not=warning:' %s +// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.osx.cocoa.Dealloc -fblocks -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.osx.cocoa.Dealloc -fblocks -triple x86_64-apple-darwin10 -fobjc-arc -fobjc-runtime-has-weak -verify %s #define nil ((id)0) @@ -11,6 +11,11 @@ #define WEAK_ON_ARC __weak #endif +// No diagnostics expected under ARC. +#if !NON_ARC + // expected-no-diagnostics +#endif + typedef signed char BOOL; @protocol NSObject - (BOOL)isEqual:(id)object; @@ -18,6 +23,7 @@ - (Class)class; @end @interface NSObject {} ++ (instancetype)alloc; - (void)dealloc; - (id)init; - (id)retain; @@ -26,7 +32,6 @@ - (oneway void)release; typedef struct objc_selector *SEL; -//===------------------------------------------------------------------------=== // Do not warn about missing release in -dealloc for ivars. @interface MyIvarClass1 : NSObject { @@ -87,11 +92,9 @@ - (void)setIvar:(NSObject *)ivar } @end -//===------------------------------------------------------------------------=== // Warn about missing release in -dealloc for properties. @interface MyPropertyClass1 : NSObject -// CHECK: DeallocMissingRelease.m:[[@LINE+1]]:1: warning: The '_ivar' instance variable in 'MyPropertyClass1' was retained by a synthesized property but was not released in 'dealloc' @property (copy) NSObject *ivar; @end @@ -99,13 +102,12 @@ @implementation MyPropertyClass1 - (void)dealloc { #if NON_ARC - [super dealloc]; + [super dealloc]; // expected-warning {{The '_ivar' ivar in 'MyPropertyClass1' was copied by a synthesized property but not released before '[super dealloc]'}} #endif } @end @interface MyPropertyClass2 : NSObject -// CHECK: DeallocMissingRelease.m:[[@LINE+1]]:1: warning: The '_ivar' instance variable in 'MyPropertyClass2' was retained by a synthesized property but was not released in 'dealloc' @property (retain) NSObject *ivar; @end @@ -113,7 +115,7 @@ @implementation MyPropertyClass2 - (void)dealloc { #if NON_ARC - [super dealloc]; + [super dealloc]; // expected-warning {{The '_ivar' ivar in 'MyPropertyClass2' was retained by a synthesized property but not released before '[super dealloc]'}} #endif } @end @@ -125,14 +127,14 @@ @interface MyPropertyClass3 : NSObject { @end @implementation MyPropertyClass3 -// CHECK: DeallocMissingRelease.m:[[@LINE+1]]:1: warning: The '_ivar' instance variable in 'MyPropertyClass3' was retained by a synthesized property but was not released in 'dealloc' @synthesize ivar = _ivar; - (void)dealloc { #if NON_ARC - [super dealloc]; + [super dealloc]; // expected-warning {{The '_ivar' ivar in 'MyPropertyClass3' was retained by a synthesized property but not released before '[super dealloc]'}} #endif } + @end @interface MyPropertyClass4 : NSObject { @@ -142,12 +144,11 @@ @interface MyPropertyClass4 : NSObject { @end @implementation MyPropertyClass4 -// CHECK: DeallocMissingRelease.m:[[@LINE+1]]:1: warning: The '_blockPropertyIvar' instance variable in 'MyPropertyClass4' was retained by a synthesized property but was not released in 'dealloc' @synthesize blockProperty = _blockPropertyIvar; - (void)dealloc { #if NON_ARC - [super dealloc]; + [super dealloc]; // expected-warning {{The '_blockPropertyIvar' ivar in 'MyPropertyClass4' was copied by a synthesized property but not released before '[super dealloc]'}} #endif } @end @@ -159,16 +160,77 @@ @interface MyPropertyClass5 : NSObject { @end @implementation MyPropertyClass5 -@synthesize ivar = _ivar; // no-warning +@synthesize ivar = _ivar; - (void)dealloc { #if NON_ARC + [super dealloc]; // no-warning because it is a weak property +#endif +} +@end + +@interface MyPropertyClassWithReturnInDealloc : NSObject { + NSObject *_ivar; +} +@property (retain) NSObject *ivar; +@end + +@implementation MyPropertyClassWithReturnInDealloc +@synthesize ivar = _ivar; +- (void)dealloc +{ + return; +#if NON_ARC + // expected-warning@-2{{The '_ivar' ivar in 'MyPropertyClassWithReturnInDealloc' was retained by a synthesized property but not released before '[super dealloc]'}} [super dealloc]; #endif } @end -//===------------------------------------------------------------------------=== +@interface MyPropertyClassWithReleaseInOtherInstance : NSObject { + NSObject *_ivar; + MyPropertyClassWithReleaseInOtherInstance *_other; +} +@property (retain) NSObject *ivar; + +-(void)releaseIvars; +@end + +@implementation MyPropertyClassWithReleaseInOtherInstance +@synthesize ivar = _ivar; + +-(void)releaseIvars; { +#if NON_ARC + [_ivar release]; +#endif +} + +- (void)dealloc +{ + [_other releaseIvars]; +#if NON_ARC + [super dealloc]; // expected-warning {{The '_ivar' ivar in 'MyPropertyClassWithReleaseInOtherInstance' was retained by a synthesized property but not released before '[super dealloc]'}} +#endif +} +@end + +@interface MyPropertyClassWithNeitherReturnNorSuperDealloc : NSObject { + NSObject *_ivar; +} +@property (retain) NSObject *ivar; +@end + +@implementation MyPropertyClassWithNeitherReturnNorSuperDealloc +@synthesize ivar = _ivar; +- (void)dealloc +{ +} +#if NON_ARC + // expected-warning@-2 {{method possibly missing a [super dealloc] call}} (From Sema) + // expected-warning@-3{{The '_ivar' ivar in 'MyPropertyClassWithNeitherReturnNorSuperDealloc' was retained by a synthesized property but not released before '[super dealloc]'}} +#endif +@end + // : 'myproperty' has kind 'assign' and thus the // assignment through the setter does not perform a release. @@ -202,51 +264,419 @@ - (void)dealloc; { // We really should warn because there is a path through -dealloc on which // _ivar2 is not released. #if NON_ARC - [_ivar2 release]; // no-warning + [_ivar2 release]; #endif } #if NON_ARC - [super dealloc]; + [super dealloc]; // expected-warning {{The '_ivar2' ivar in 'ClassWithControlFlowInRelease' was retained by a synthesized property but not released before '[super dealloc]'}} #endif } - @end -//===------------------------------------------------------------------------=== // Don't warn when the property is nil'd out in -dealloc @interface ClassWithNildOutProperty : NSObject -@property (retain) NSObject *ivar; // no-warning +@property (retain) NSObject *ivar; +@property (assign) int *intPtrProp; @end @implementation ClassWithNildOutProperty - (void)dealloc; { self.ivar = nil; + // Make sure to handle setting a non-retainable property to 0. + self.intPtrProp = 0; #if NON_ARC - [super dealloc]; + [super dealloc]; // no-warning +#endif +} +@end + +// Do warn when the ivar but not the property is nil'd out in -dealloc + +@interface ClassWithNildOutIvar : NSObject +@property (retain) NSObject *ivar; +@end + +@implementation ClassWithNildOutIvar +- (void)dealloc; { + // Oops. Meant self.ivar = nil + _ivar = nil; + +#if NON_ARC + [super dealloc]; // expected-warning {{The '_ivar' ivar in 'ClassWithNildOutIvar' was retained by a synthesized property but not released before '[super dealloc]'}} #endif } +@end +// Do warn when the ivar is updated to a different value that is then +// released. + +@interface ClassWithUpdatedIvar : NSObject +@property (retain) NSObject *ivar; @end -//===------------------------------------------------------------------------=== +@implementation ClassWithUpdatedIvar +- (void)dealloc; { + _ivar = [[NSObject alloc] init]; + +#if NON_ARC + [_ivar release]; +#endif + +#if NON_ARC + [super dealloc]; // expected-warning {{The '_ivar' ivar in 'ClassWithUpdatedIvar' was retained by a synthesized property but not released before '[super dealloc]'}} +#endif +} +@end + + // Don't warn when the property is nil'd out with a setter in -dealloc @interface ClassWithNildOutPropertyViaSetter : NSObject -@property (retain) NSObject *ivar; // no-warning +@property (retain) NSObject *ivar; @end @implementation ClassWithNildOutPropertyViaSetter - (void)dealloc; { [self setIvar:nil]; +#if NON_ARC + [super dealloc]; // no-warning +#endif +} +@end + + +// Don't warn about missing releases when -dealloc helpers are called. + +@interface ClassWithDeallocHelpers : NSObject +@property (retain) NSObject *ivarReleasedInMethod; +@property (retain) NSObject *propNilledOutInMethod; + +@property (retain) NSObject *ivarReleasedInFunction; +@property (retain) NSObject *propNilledOutInFunction; + +@property (retain) NSObject *ivarNeverReleased; +- (void)invalidateInMethod; +@end + +void ReleaseValueHelper(NSObject *iv) { +#if NON_ARC + [iv release]; +#endif +} + +void NilOutPropertyHelper(ClassWithDeallocHelpers *o) { + o.propNilledOutInFunction = nil; +} + +@implementation ClassWithDeallocHelpers +- (void)invalidateInMethod { +#if NON_ARC + [_ivarReleasedInMethod release]; +#endif + self.propNilledOutInMethod = nil; +} + +- (void)dealloc; { + ReleaseValueHelper(_ivarReleasedInFunction); + NilOutPropertyHelper(self); + + [self invalidateInMethod]; +#if NON_ARC + [super dealloc]; // expected-warning {{The '_ivarNeverReleased' ivar in 'ClassWithDeallocHelpers' was retained by a synthesized property but not released before '[super dealloc]'}} +#endif +} +@end + + +// Don't warn when self in -dealloc escapes. + +@interface ClassWhereSelfEscapesViaMethodCall : NSObject +@property (retain) NSObject *ivar; // no-warning +@end + +@interface ClassWhereSelfEscapesViaMethodCall (Other) +- (void)invalidate; // In other translation unit. +@end + +@implementation ClassWhereSelfEscapesViaMethodCall +- (void)dealloc; { + [self invalidate]; +#if NON_ARC + [super dealloc]; +#endif +} // no-warning +@end + +@interface ClassWhereSelfEscapesViaPropertyAccess : NSObject +@property (retain) NSObject *ivar; +@end + +@interface ClassWhereSelfEscapesViaPropertyAccess (Other) +// The implementation of this property is unknown and therefore could +// release ivar. +@property (retain) NSObject *otherIvar; +@end + +@implementation ClassWhereSelfEscapesViaPropertyAccess +- (void)dealloc; { + self.otherIvar = nil; #if NON_ARC [super dealloc]; #endif +} // no-warning +@end + +// Don't treat self as escaping when setter called on *synthesized* +// property. + +@interface ClassWhereSelfEscapesViaSynthesizedPropertyAccess : NSObject +@property (retain) NSObject *ivar; +@property (retain) NSObject *otherIvar; +@end + +@implementation ClassWhereSelfEscapesViaSynthesizedPropertyAccess +- (void)dealloc; { + self.otherIvar = nil; +#if NON_ARC + [super dealloc]; // expected-warning {{The '_ivar' ivar in 'ClassWhereSelfEscapesViaSynthesizedPropertyAccess' was retained by a synthesized property but not released before '[super dealloc]'}} +#endif } +@end + +// Don't warn when value escapes. +@interface ClassWhereIvarValueEscapes : NSObject +@property (retain) NSObject *ivar; +@end + +void ReleaseMe(id arg); + +@implementation ClassWhereIvarValueEscapes +- (void)dealloc; { + + ReleaseMe(_ivar); + +#if NON_ARC + [super dealloc]; +#endif +} // no-warning +@end + +// Don't warn when value is known to be nil. + +@interface ClassWhereIvarIsNil : NSObject +@property (retain) NSObject *ivarIsNil; +@end + +@implementation ClassWhereIvarIsNil +- (void)dealloc; { + +#if NON_ARC + if (_ivarIsNil) + [_ivarIsNil release]; + + [super dealloc]; +#endif +} // no-warning +@end + + +// Don't warn for non-retainable properties. + +@interface ClassWithNonRetainableProperty : NSObject +@property (assign) int *ivar; // no-warning +@end + +@implementation ClassWithNonRetainableProperty +- (void)dealloc; { +#if NON_ARC + [super dealloc]; +#endif +} // no-warning +@end + + +@interface SuperClassOfClassWithInlinedSuperDealloc : NSObject +@property (retain) NSObject *propInSuper; +@end + +@implementation SuperClassOfClassWithInlinedSuperDealloc +- (void)dealloc { +#if NON_ARC + [super dealloc]; // expected-warning {{The '_propInSuper' ivar in 'SuperClassOfClassWithInlinedSuperDealloc' was retained by a synthesized property but not released before '[super dealloc]'}} +#endif +} +@end + +@interface ClassWithInlinedSuperDealloc : SuperClassOfClassWithInlinedSuperDealloc +@property (retain) NSObject *propInSub; +@end + +@implementation ClassWithInlinedSuperDealloc +- (void)dealloc { +#if NON_ARC + [super dealloc]; // expected-warning {{The '_propInSub' ivar in 'ClassWithInlinedSuperDealloc' was retained by a synthesized property but not released before '[super dealloc]'}} +#endif +} +@end + + +@interface SuperClassOfClassWithInlinedSuperDeallocAndInvalidation : NSObject +@property (retain) NSObject *propInSuper; + +- (void)invalidate; +@end + +@implementation SuperClassOfClassWithInlinedSuperDeallocAndInvalidation + +- (void)invalidate { +#if NON_ARC + [_propInSuper release]; +#endif + _propInSuper = nil; +} + +- (void)dealloc { + [self invalidate]; +#if NON_ARC + [super dealloc]; // no-warning +#endif +} +@end + +@interface ClassWithInlinedSuperDeallocAndInvalidation : SuperClassOfClassWithInlinedSuperDeallocAndInvalidation +@property (retain) NSObject *propInSub; +@end + +@implementation ClassWithInlinedSuperDeallocAndInvalidation + +- (void)invalidate { +#if NON_ARC + [_propInSub release]; +#endif + [super invalidate]; +} + +- (void)dealloc { +#if NON_ARC + [super dealloc]; // no-warning +#endif +} +@end + + +@interface SuperClassOfClassThatEscapesBeforeInliningSuper : NSObject +@property (retain) NSObject *propInSuper; +@end + +@implementation SuperClassOfClassThatEscapesBeforeInliningSuper + +- (void)dealloc { + +#if NON_ARC + [super dealloc]; // expected-warning {{The '_propInSuper' ivar in 'SuperClassOfClassThatEscapesBeforeInliningSuper' was retained by a synthesized property but not released before '[super dealloc]'}} +#endif +} +@end + +@interface ClassThatEscapesBeforeInliningSuper : SuperClassOfClassThatEscapesBeforeInliningSuper +@property (retain) NSObject *propInSub; +@end + +@interface ClassThatEscapesBeforeInliningSuper (Other) +- (void)invalidate; // No implementation in translation unit. +@end + +@implementation ClassThatEscapesBeforeInliningSuper +- (void)dealloc { + [self invalidate]; + +#if NON_ARC + [super dealloc]; // no-warning +#endif +} +@end + + +#if NON_ARC +@interface ReleaseIvarInField : NSObject { + int _tag; + union { + NSObject *field1; + NSObject *field2; + } _someUnion; + + struct { + NSObject *field1; + } _someStruct; +} +@end + +@implementation ReleaseIvarInField +- (void)dealloc { + if (_tag) { + [_someUnion.field1 release]; + } else { + [_someUnion.field2 release]; + } + + [_someStruct.field1 release]; + [super dealloc]; +} +@end +#endif + +#if NON_ARC +@interface ReleaseIvarInArray : NSObject { + NSObject *_array[3]; +} +@end + +@implementation ReleaseIvarInArray +- (void)dealloc { + for (int i = 0; i < 3; i++) { + [_array[i] release]; + } + [super dealloc]; +} +@end +#endif + +// Don't warn about missing releases for subclasses of SenTestCase or +// for classes that are not subclasses of NSObject. + +@interface SenTestCase : NSObject {} +@end + +@interface MyClassTest : SenTestCase +@property (retain) NSObject *ivar; +@end + +@implementation MyClassTest +-(void)tearDown { +#if NON_ARC + [_ivar release]; +#endif +} + +-(void)dealloc; { +#if NON_ARC + [super dealloc]; // no-warning +#endif +} +@end + +__attribute__((objc_root_class)) +@interface NonNSObjectMissingDealloc +@property (retain) NSObject *ivar; +@end +@implementation NonNSObjectMissingDealloc +-(void)dealloc; { + +} @end -// CHECK: 4 warnings generated. diff --git a/test/Analysis/MissingDealloc.m b/test/Analysis/MissingDealloc.m index d6af44b895b..ae880546ff9 100644 --- a/test/Analysis/MissingDealloc.m +++ b/test/Analysis/MissingDealloc.m @@ -128,6 +128,9 @@ @interface SenTestCase : NSObject {} @interface MyClassTest : SenTestCase { NSString *resourcePath; } + +@property (retain) NSObject *ivar; + @end @interface NSBundle : NSObject {} @@ -143,4 +146,15 @@ - (void)testXXX { // do something which uses resourcepath } @end + +//===------------------------------------------------------------------------=== +// Don't warn for clases that aren't subclasses of NSObject + +__attribute__((objc_root_class)) +@interface NonNSObjectMissingDealloc +@property (retain) NSObject *ivar; +@end +@implementation NonNSObjectMissingDealloc +@end + // CHECK: 4 warnings generated. diff --git a/test/Analysis/PR2978.m b/test/Analysis/PR2978.m index 2067b3e85af..bb0fc414566 100644 --- a/test/Analysis/PR2978.m +++ b/test/Analysis/PR2978.m @@ -5,7 +5,7 @@ @interface NSObject - (void)release; -- dealloc; +- (void)dealloc; @end @interface MyClass : NSObject { @@ -19,8 +19,14 @@ @interface MyClass : NSObject { id _M; id _P; id _Q; + id _R; + id _S; id _V; id _W; + + MyClass *_other; + + id _nonPropertyIvar; } @property(retain) id X; @property(retain) id Y; @@ -29,8 +35,13 @@ @interface MyClass : NSObject { @property(weak) id L; @property(readonly) id N; @property(retain) id M; -@property(weak) id P; // expected-warning {{'_P' instance variable in 'MyClass' was not retained by a synthesized property but was released in 'dealloc'}} +@property(weak) id P; @property(weak) id Q; +@property(retain) id R; +@property(weak, readonly) id S; + +@property(assign, readonly) id T; // Shadowed in class extension +@property(assign) id U; @property(retain) id V; @property(retain) id W; @@ -38,36 +49,67 @@ -(id) O; -(void) setO: (id) arg; @end +@interface MyClass () +// Shadows T to make it readwrite internally but readonly externally. +@property(assign, readwrite) id T; +@end + @implementation MyClass @synthesize X = _X; -@synthesize Y = _Y; // expected-warning{{The '_Y' instance variable in 'MyClass' was retained by a synthesized property but was not released in 'dealloc'}} -@synthesize Z = _Z; // expected-warning{{The '_Z' instance variable in 'MyClass' was not retained by a synthesized property but was released in 'dealloc'}} +@synthesize Y = _Y; +@synthesize Z = _Z; @synthesize K = _K; -@synthesize L = _L; // no-warning -@synthesize N = _N; // no-warning +@synthesize L = _L; +@synthesize N = _N; @synthesize M = _M; -@synthesize Q = _Q; // expected-warning {{'_Q' instance variable in 'MyClass' was not retained by a synthesized property but was released in 'dealloc'}} +@synthesize Q = _Q; +@synthesize R = _R; @synthesize V = _V; -@synthesize W = _W; // expected-warning{{The '_W' instance variable in 'MyClass' was retained by a synthesized property but was not released in 'dealloc'}} +@synthesize W = _W; -(id) O{ return 0; } -(void) setO:(id)arg { } -- (id)dealloc + +-(void) releaseInHelper { + [_R release]; // no-warning + _R = @"Hi"; +} + +- (void)dealloc { + [_X release]; - [_Z release]; + [_Z release]; // expected-warning{{The '_Z' ivar in 'MyClass' was synthesized for an assign, readwrite property but was released in 'dealloc'}} + [_T release]; // no-warning + + [_other->_Z release]; // no-warning [_N release]; - + self.M = 0; // This will release '_M' [self setV:0]; // This will release '_V' [self setW:@"newW"]; // This will release '_W', but retain the new value - self.O = 0; // no-warning - [_Q release]; + [_S release]; // expected-warning {{The '_S' ivar in 'MyClass' was synthesized for a weak property but was released in 'dealloc'}} + + self.O = 0; // no-warning + + [_Q release]; // expected-warning {{The '_Q' ivar in 'MyClass' was synthesized for a weak property but was released in 'dealloc'}} + self.P = 0; + + [self releaseInHelper]; + + [_nonPropertyIvar release]; // no-warning + + // Silly, but not an error. + if (!_U) + [_U release]; + [super dealloc]; - return 0; + // expected-warning@-1{{The '_Y' ivar in 'MyClass' was retained by a synthesized property but not released before '[super dealloc]'}} + // expected-warning@-2{{The '_W' ivar in 'MyClass' was retained by a synthesized property but not released before '[super dealloc]'}} + } @end diff --git a/test/Analysis/properties.m b/test/Analysis/properties.m index d92f1a1a7e8..98d6d7ce1d7 100644 --- a/test/Analysis/properties.m +++ b/test/Analysis/properties.m @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount,debug.ExprInspection -analyzer-store=region -verify -Wno-objc-root-class %s -// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount,debug.ExprInspection -analyzer-store=region -verify -Wno-objc-root-class -fobjc-arc %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount,alpha.osx.cocoa.Dealloc,debug.ExprInspection -analyzer-store=region -verify -Wno-objc-root-class %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount,alpha.osx.cocoa.Dealloc,debug.ExprInspection -analyzer-store=region -verify -Wno-objc-root-class -fobjc-arc %s void clang_analyzer_eval(int); @@ -22,6 +22,7 @@ -(id)autorelease; -(id)copy; -(id)retain; -(oneway void)release; +-(void)dealloc; @end @interface NSString : NSObject - (NSUInteger)length; @@ -138,6 +139,14 @@ @interface Person : NSObject { @implementation Person @synthesize name = _name; + +-(void)dealloc { +#if !__has_feature(objc_arc) + self.name = [[NSString alloc] init]; // expected-warning {{leak}} + + [super dealloc]; // expected-warning {{The '_name' ivar in 'Person' was retained by a synthesized property but not released before '[super dealloc]}} +#endif +} @end #if !__has_feature(objc_arc) From d123f57860bf6b3212b4d90b9f6d181b3e139686 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Thu, 25 Feb 2016 23:36:52 +0000 Subject: [PATCH 228/742] [analyzer] Warn on use of 'self' after call to to [super dealloc]. Referring to 'self' after a call to [super dealloc] is a use-after-free in Objective-C because NSObject's -dealloc frees the memory pointed to by self. This patch extends the ObjCSuperDeallocChecker to catch this error. rdar://problem/6953275 Differential Revision: http://reviews.llvm.org/D17528 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261935 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit b6d0b32d8e303d32d346400f307e298e40e2a51c) --- .../Checkers/ObjCSuperDeallocChecker.cpp | 143 +++++++++++++++--- test/Analysis/DeallocUseAfterFreeErrors.m | 100 +++++++++--- 2 files changed, 200 insertions(+), 43 deletions(-) diff --git a/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp index d1d6ef388db..596ad70c423 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// // // This defines ObjCSuperDeallocChecker, a builtin check that warns when -// [super dealloc] is called twice on the same instance in MRR mode. +// self is used after a call to [super dealloc] in MRR mode. // //===----------------------------------------------------------------------===// @@ -25,7 +25,8 @@ using namespace ento; namespace { class ObjCSuperDeallocChecker - : public Checker { + : public Checker { mutable IdentifierInfo *IIdealloc, *IINSObject; mutable Selector SELdealloc; @@ -40,12 +41,24 @@ class ObjCSuperDeallocChecker ObjCSuperDeallocChecker(); void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; + + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + + void checkLocation(SVal l, bool isLoad, const Stmt *S, + CheckerContext &C) const; + +private: + + void diagnoseCallArguments(const CallEvent &CE, CheckerContext &C) const; + + void reportUseAfterDealloc(SymbolRef Sym, StringRef Desc, const Stmt *S, + CheckerContext &C) const; }; } // End anonymous namespace. // Remember whether [super dealloc] has previously been called on the -// a SymbolRef for the receiver. +// SymbolRef for the receiver. REGISTER_SET_WITH_PROGRAMSTATE(CalledSuperDealloc, SymbolRef) class SuperDeallocBRVisitor final @@ -71,40 +84,36 @@ class SuperDeallocBRVisitor final void ObjCSuperDeallocChecker::checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const { - if (!isSuperDeallocMessage(M)) - return; ProgramStateRef State = C.getState(); SymbolRef ReceiverSymbol = M.getReceiverSVal().getAsSymbol(); - assert(ReceiverSymbol && "No receiver symbol at call to [super dealloc]?"); + if (!ReceiverSymbol) { + diagnoseCallArguments(M, C); + return; + } bool AlreadyCalled = State->contains(ReceiverSymbol); - - // If [super dealloc] has not been called, there is nothing to do. We'll - // note the fact that [super dealloc] was called in checkPostObjCMessage. if (!AlreadyCalled) return; - // We have a duplicate [super dealloc] method call. - // This likely causes a crash, so stop exploring the - // path by generating a sink. - ExplodedNode *ErrNode = C.generateErrorNode(); - // If we've already reached this node on another path, return. - if (!ErrNode) - return; + StringRef Desc; - // Generate the report. - std::unique_ptr BR( - new BugReport(*DoubleSuperDeallocBugType, - "[super dealloc] should not be called multiple times", - ErrNode)); - BR->addRange(M.getOriginExpr()->getSourceRange()); - BR->addVisitor(llvm::make_unique(ReceiverSymbol)); - C.emitReport(std::move(BR)); + if (isSuperDeallocMessage(M)) { + Desc = "[super dealloc] should not be called multiple times"; + } else { + Desc = StringRef(); + } + + reportUseAfterDealloc(ReceiverSymbol, Desc, M.getOriginExpr(), C); return; } +void ObjCSuperDeallocChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + diagnoseCallArguments(Call, C); +} + void ObjCSuperDeallocChecker::checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const { // Check for [super dealloc] method call. @@ -122,6 +131,92 @@ void ObjCSuperDeallocChecker::checkPostObjCMessage(const ObjCMethodCall &M, C.addTransition(State); } +void ObjCSuperDeallocChecker::checkLocation(SVal L, bool IsLoad, const Stmt *S, + CheckerContext &C) const { + SymbolRef BaseSym = L.getLocSymbolInBase(); + if (!BaseSym) + return; + + ProgramStateRef State = C.getState(); + + if (!State->contains(BaseSym)) + return; + + const MemRegion *R = L.getAsRegion(); + if (!R) + return; + + // Climb the super regions to find the base symbol while recording + // the second-to-last region for error reporting. + const MemRegion *PriorSubRegion = nullptr; + while (const SubRegion *SR = dyn_cast(R)) { + if (const SymbolicRegion *SymR = dyn_cast(SR)) { + BaseSym = SymR->getSymbol(); + break; + } else { + R = SR->getSuperRegion(); + PriorSubRegion = SR; + } + } + + StringRef Desc = StringRef(); + auto *IvarRegion = dyn_cast_or_null(PriorSubRegion); + + if (IvarRegion) { + std::string Buf; + llvm::raw_string_ostream OS(Buf); + OS << "use of instance variable '" << *IvarRegion->getDecl() << + "' after the instance has been freed with call to [super dealloc]"; + Desc = OS.str(); + } + + reportUseAfterDealloc(BaseSym, Desc, S, C); +} + +/// Report a use-after-dealloc on \param Sym. If not empty, +/// \param Desc will be used to describe the error; otherwise, +/// a default warning will be used. +void ObjCSuperDeallocChecker::reportUseAfterDealloc(SymbolRef Sym, + StringRef Desc, + const Stmt *S, + CheckerContext &C) const { + // We have a use of self after free. + // This likely causes a crash, so stop exploring the + // path by generating a sink. + ExplodedNode *ErrNode = C.generateErrorNode(); + // If we've already reached this node on another path, return. + if (!ErrNode) + return; + + if (Desc.empty()) + Desc = "use of 'self' after it has been freed with call to [super dealloc]"; + + // Generate the report. + std::unique_ptr BR( + new BugReport(*DoubleSuperDeallocBugType, Desc, ErrNode)); + BR->addRange(S->getSourceRange()); + BR->addVisitor(llvm::make_unique(Sym)); + C.emitReport(std::move(BR)); +} + +/// Diagnose if any of the arguments to \param CE have already been +/// dealloc'd. +void ObjCSuperDeallocChecker::diagnoseCallArguments(const CallEvent &CE, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + unsigned ArgCount = CE.getNumArgs(); + for (unsigned I = 0; I < ArgCount; I++) { + SymbolRef Sym = CE.getArgSVal(I).getAsSymbol(); + if (!Sym) + continue; + + if (State->contains(Sym)) { + reportUseAfterDealloc(Sym, StringRef(), CE.getArgExpr(I), C); + return; + } + } +} + ObjCSuperDeallocChecker::ObjCSuperDeallocChecker() : IIdealloc(nullptr), IINSObject(nullptr) { diff --git a/test/Analysis/DeallocUseAfterFreeErrors.m b/test/Analysis/DeallocUseAfterFreeErrors.m index 75c2a1e573f..9ae9a96718c 100644 --- a/test/Analysis/DeallocUseAfterFreeErrors.m +++ b/test/Analysis/DeallocUseAfterFreeErrors.m @@ -35,8 +35,9 @@ - (instancetype)initWithIvar:(NSObject *)ivar { return self; } - (void)dealloc { - [super dealloc]; - [_ivar release]; + [super dealloc]; // expected-note {{[super dealloc] called here}} + [_ivar release]; // expected-warning {{use of instance variable '_ivar' after the instance has been freed with call to [super dealloc]}} + // expected-note@-1 {{use of instance variable '_ivar' after the instance has been freed with call to [super dealloc]}} } @end @@ -54,8 +55,46 @@ - (instancetype)initWithDelegate:(NSObject *)delegate { return self; } - (void)dealloc { - [super dealloc]; - _delegate = nil; + [super dealloc]; // expected-note {{[super dealloc] called here}} + _delegate = nil; // expected-warning {{use of instance variable '_delegate' after the instance has been freed with call to [super dealloc]}} + // expected-note@-1 {{use of instance variable '_delegate' after the instance has been freed with call to [super dealloc]}} +} +@end + + +struct SomeStruct { + int f; +}; + +@interface SuperDeallocThenAssignIvarField : NSObject { + struct SomeStruct _s; +} +@end + +@implementation SuperDeallocThenAssignIvarField +- (void)dealloc { + [super dealloc]; // expected-note {{[super dealloc] called here}} + _s.f = 7; // expected-warning {{use of instance variable '_s' after the instance has been freed with call to [super dealloc]}} + // expected-note@-1 {{use of instance variable '_s' after the instance has been freed with call to [super dealloc]}} +} +@end + +@interface OtherClassWithIvar { +@public + int _otherIvar; +} +@end; + +@interface SuperDeallocThenAssignIvarIvar : NSObject { + OtherClassWithIvar *_ivar; +} +@end + +@implementation SuperDeallocThenAssignIvarIvar +- (void)dealloc { + [super dealloc]; // expected-note {{[super dealloc] called here}} + _ivar->_otherIvar = 7; // expected-warning {{use of instance variable '_ivar' after the instance has been freed with call to [super dealloc]}} + // expected-note@-1 {{use of instance variable '_ivar' after the instance has been freed with call to [super dealloc]}} } @end @@ -72,8 +111,9 @@ - (instancetype)initWithProperty:(NSObject *)ivar { return self; } - (void)dealloc { - [super dealloc]; - self.ivar = nil; + [super dealloc]; // expected-note {{[super dealloc] called here}} + self.ivar = nil; // expected-warning {{use of 'self' after it has been freed with call to [super dealloc]}} + // expected-note@-1 {{use of 'self' after it has been freed with call to [super dealloc]}} } @end @@ -90,8 +130,9 @@ - (instancetype)initWithDelegate:(NSObject *)delegate { return self; } - (void)dealloc { - [super dealloc]; - self.delegate = nil; + [super dealloc]; // expected-note {{[super dealloc] called here}} + self.delegate = nil; // expected-warning {{use of 'self' after it has been freed with call to [super dealloc]}} + // expected-note@-1 {{use of 'self' after it has been freed with call to [super dealloc]}} } @end @@ -103,8 +144,9 @@ @implementation SuperDeallocThenCallInstanceMethodClass - (void)_invalidate { } - (void)dealloc { - [super dealloc]; - [self _invalidate]; + [super dealloc]; // expected-note {{[super dealloc] called here}} + [self _invalidate]; // expected-warning {{use of 'self' after it has been freed with call to [super dealloc]}} + // expected-note@-1 {{use of 'self' after it has been freed with call to [super dealloc]}} } @end @@ -117,8 +159,23 @@ static void _invalidate(NSObject *object) { @implementation SuperDeallocThenCallNonObjectiveCMethodClass - (void)dealloc { - [super dealloc]; - _invalidate(self); + [super dealloc]; // expected-note {{[super dealloc] called here}} + _invalidate(self); // expected-warning {{use of 'self' after it has been freed with call to [super dealloc]}} + // expected-note@-1 {{use of 'self' after it has been freed with call to [super dealloc]}} +} +@end + +@interface SuperDeallocThenCallObjectiveClassMethodClass : NSObject { } +@end + +@implementation SuperDeallocThenCallObjectiveClassMethodClass ++ (void) invalidate:(id)arg; { +} + +- (void)dealloc { + [super dealloc]; // expected-note {{[super dealloc] called here}} + [SuperDeallocThenCallObjectiveClassMethodClass invalidate:self]; // expected-warning {{use of 'self' after it has been freed with call to [super dealloc]}} + // expected-note@-1 {{use of 'self' after it has been freed with call to [super dealloc]}} } @end @@ -132,13 +189,14 @@ @implementation TwoSuperDeallocCallsClass - (void)_invalidate { } - (void)dealloc { - if (_ivar) { + if (_ivar) { // expected-note {{Taking false branch}} [_ivar release]; [super dealloc]; return; } - [super dealloc]; - [self _invalidate]; + [super dealloc]; // expected-note {{[super dealloc] called here}} + [self _invalidate]; // expected-warning {{use of 'self' after it has been freed with call to [super dealloc]}} + // expected-note@-1 {{use of 'self' after it has been freed with call to [super dealloc]}} } @end @@ -244,11 +302,15 @@ + (void)dealloc { // Do not warn about calling [super dealloc] twice if when the analyzer has // inlined the call to its super deallocator. -@interface SuperClassCallingSuperDealloc : NSObject +@interface SuperClassCallingSuperDealloc : NSObject { + NSObject *_ivar; +} @end @implementation SuperClassCallingSuperDealloc - (void)dealloc; { + [_ivar release]; // no-warning + [super dealloc]; } @end @@ -291,8 +353,8 @@ - (void)anotherMethod { - (void)dealloc; { [super dealloc]; // expected-note {{[super dealloc] called here}} - [self anotherMethod]; - [super dealloc]; // expected-warning {{[super dealloc] should not be called multiple times}} - // expected-note@-1 {{[super dealloc] should not be called multiple times}} + [self anotherMethod]; // expected-warning {{use of 'self' after it has been freed with call to [super dealloc]}} + // expected-note@-1 {{use of 'self' after it has been freed with call to [super dealloc]}} + [super dealloc]; } @end From e01508aecd7b188dd9e1cd4c10f9408e4cf781b8 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Fri, 26 Feb 2016 00:23:41 +0000 Subject: [PATCH 229/742] [analyzer] Fix a memory error in r261935 caught by the Windows bots. It was using a temporary StringRef after its underlying storage was freed. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261944 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit a325236d5d6232fda5902d58946397d404d5aa89) --- lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp index 596ad70c423..263da201465 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp @@ -162,9 +162,9 @@ void ObjCSuperDeallocChecker::checkLocation(SVal L, bool IsLoad, const Stmt *S, StringRef Desc = StringRef(); auto *IvarRegion = dyn_cast_or_null(PriorSubRegion); + std::string Buf; + llvm::raw_string_ostream OS(Buf); if (IvarRegion) { - std::string Buf; - llvm::raw_string_ostream OS(Buf); OS << "use of instance variable '" << *IvarRegion->getDecl() << "' after the instance has been freed with call to [super dealloc]"; Desc = OS.str(); From 07b8a58b8e627380766e98c2a47b9c484e514047 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Fri, 26 Feb 2016 00:47:42 +0000 Subject: [PATCH 230/742] [analyzer] Shorten ObjcSuperDeallocChecker diagnostics. Change "use of 'self' after it has been freed with call to [super dealloc]" to "use of 'self' after it has been deallocated" and "use of instance variable '_ivar' after the instance has been freed with call to [super dealloc]" to "use of instance variable '_ivar' after 'self' has been deallocated". git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261945 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit c7025a697597480f4aae15df9914a37c2279cb78) --- .../Checkers/ObjCSuperDeallocChecker.cpp | 4 +- test/Analysis/DeallocUseAfterFreeErrors.m | 57 ++++++++++++------- 2 files changed, 37 insertions(+), 24 deletions(-) diff --git a/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp index 263da201465..9c1d69d2fe2 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp @@ -166,7 +166,7 @@ void ObjCSuperDeallocChecker::checkLocation(SVal L, bool IsLoad, const Stmt *S, llvm::raw_string_ostream OS(Buf); if (IvarRegion) { OS << "use of instance variable '" << *IvarRegion->getDecl() << - "' after the instance has been freed with call to [super dealloc]"; + "' after 'self' has been deallocated"; Desc = OS.str(); } @@ -189,7 +189,7 @@ void ObjCSuperDeallocChecker::reportUseAfterDealloc(SymbolRef Sym, return; if (Desc.empty()) - Desc = "use of 'self' after it has been freed with call to [super dealloc]"; + Desc = "use of 'self' after it has been deallocated"; // Generate the report. std::unique_ptr BR( diff --git a/test/Analysis/DeallocUseAfterFreeErrors.m b/test/Analysis/DeallocUseAfterFreeErrors.m index 9ae9a96718c..cfb202b6507 100644 --- a/test/Analysis/DeallocUseAfterFreeErrors.m +++ b/test/Analysis/DeallocUseAfterFreeErrors.m @@ -36,8 +36,8 @@ - (instancetype)initWithIvar:(NSObject *)ivar { } - (void)dealloc { [super dealloc]; // expected-note {{[super dealloc] called here}} - [_ivar release]; // expected-warning {{use of instance variable '_ivar' after the instance has been freed with call to [super dealloc]}} - // expected-note@-1 {{use of instance variable '_ivar' after the instance has been freed with call to [super dealloc]}} + [_ivar release]; // expected-warning {{use of instance variable '_ivar' after 'self' has been deallocated}} + // expected-note@-1 {{use of instance variable '_ivar' after 'self' has been deallocated}} } @end @@ -56,8 +56,8 @@ - (instancetype)initWithDelegate:(NSObject *)delegate { } - (void)dealloc { [super dealloc]; // expected-note {{[super dealloc] called here}} - _delegate = nil; // expected-warning {{use of instance variable '_delegate' after the instance has been freed with call to [super dealloc]}} - // expected-note@-1 {{use of instance variable '_delegate' after the instance has been freed with call to [super dealloc]}} + _delegate = nil; // expected-warning {{use of instance variable '_delegate' after 'self' has been deallocated}} + // expected-note@-1 {{use of instance variable '_delegate' after 'self' has been deallocated}} } @end @@ -74,8 +74,8 @@ @interface SuperDeallocThenAssignIvarField : NSObject { @implementation SuperDeallocThenAssignIvarField - (void)dealloc { [super dealloc]; // expected-note {{[super dealloc] called here}} - _s.f = 7; // expected-warning {{use of instance variable '_s' after the instance has been freed with call to [super dealloc]}} - // expected-note@-1 {{use of instance variable '_s' after the instance has been freed with call to [super dealloc]}} + _s.f = 7; // expected-warning {{use of instance variable '_s' after 'self' has been deallocated}} + // expected-note@-1 {{use of instance variable '_s' after 'self' has been deallocated}} } @end @@ -93,8 +93,21 @@ @interface SuperDeallocThenAssignIvarIvar : NSObject { @implementation SuperDeallocThenAssignIvarIvar - (void)dealloc { [super dealloc]; // expected-note {{[super dealloc] called here}} - _ivar->_otherIvar = 7; // expected-warning {{use of instance variable '_ivar' after the instance has been freed with call to [super dealloc]}} - // expected-note@-1 {{use of instance variable '_ivar' after the instance has been freed with call to [super dealloc]}} + _ivar->_otherIvar = 7; // expected-warning {{use of instance variable '_ivar' after 'self' has been deallocated}} + // expected-note@-1 {{use of instance variable '_ivar' after 'self' has been deallocated}} +} +@end + +@interface SuperDeallocThenAssignSelfIvar : NSObject { + NSObject *_ivar; +} +@end + +@implementation SuperDeallocThenAssignSelfIvar +- (void)dealloc { + [super dealloc]; // expected-note {{[super dealloc] called here}} + self->_ivar = nil; // expected-warning {{use of instance variable '_ivar' after 'self' has been deallocated}} + // expected-note@-1 {{use of instance variable '_ivar' after 'self' has been deallocated}} } @end @@ -112,8 +125,8 @@ - (instancetype)initWithProperty:(NSObject *)ivar { } - (void)dealloc { [super dealloc]; // expected-note {{[super dealloc] called here}} - self.ivar = nil; // expected-warning {{use of 'self' after it has been freed with call to [super dealloc]}} - // expected-note@-1 {{use of 'self' after it has been freed with call to [super dealloc]}} + self.ivar = nil; // expected-warning {{use of 'self' after it has been deallocated}} + // expected-note@-1 {{use of 'self' after it has been deallocated}} } @end @@ -131,8 +144,8 @@ - (instancetype)initWithDelegate:(NSObject *)delegate { } - (void)dealloc { [super dealloc]; // expected-note {{[super dealloc] called here}} - self.delegate = nil; // expected-warning {{use of 'self' after it has been freed with call to [super dealloc]}} - // expected-note@-1 {{use of 'self' after it has been freed with call to [super dealloc]}} + self.delegate = nil; // expected-warning {{use of 'self' after it has been deallocated}} + // expected-note@-1 {{use of 'self' after it has been deallocated}} } @end @@ -145,8 +158,8 @@ - (void)_invalidate { } - (void)dealloc { [super dealloc]; // expected-note {{[super dealloc] called here}} - [self _invalidate]; // expected-warning {{use of 'self' after it has been freed with call to [super dealloc]}} - // expected-note@-1 {{use of 'self' after it has been freed with call to [super dealloc]}} + [self _invalidate]; // expected-warning {{use of 'self' after it has been deallocated}} + // expected-note@-1 {{use of 'self' after it has been deallocated}} } @end @@ -160,8 +173,8 @@ static void _invalidate(NSObject *object) { @implementation SuperDeallocThenCallNonObjectiveCMethodClass - (void)dealloc { [super dealloc]; // expected-note {{[super dealloc] called here}} - _invalidate(self); // expected-warning {{use of 'self' after it has been freed with call to [super dealloc]}} - // expected-note@-1 {{use of 'self' after it has been freed with call to [super dealloc]}} + _invalidate(self); // expected-warning {{use of 'self' after it has been deallocated}} + // expected-note@-1 {{use of 'self' after it has been deallocated}} } @end @@ -174,8 +187,8 @@ + (void) invalidate:(id)arg; { - (void)dealloc { [super dealloc]; // expected-note {{[super dealloc] called here}} - [SuperDeallocThenCallObjectiveClassMethodClass invalidate:self]; // expected-warning {{use of 'self' after it has been freed with call to [super dealloc]}} - // expected-note@-1 {{use of 'self' after it has been freed with call to [super dealloc]}} + [SuperDeallocThenCallObjectiveClassMethodClass invalidate:self]; // expected-warning {{use of 'self' after it has been deallocated}} + // expected-note@-1 {{use of 'self' after it has been deallocated}} } @end @@ -195,8 +208,8 @@ - (void)dealloc { return; } [super dealloc]; // expected-note {{[super dealloc] called here}} - [self _invalidate]; // expected-warning {{use of 'self' after it has been freed with call to [super dealloc]}} - // expected-note@-1 {{use of 'self' after it has been freed with call to [super dealloc]}} + [self _invalidate]; // expected-warning {{use of 'self' after it has been deallocated}} + // expected-note@-1 {{use of 'self' after it has been deallocated}} } @end @@ -353,8 +366,8 @@ - (void)anotherMethod { - (void)dealloc; { [super dealloc]; // expected-note {{[super dealloc] called here}} - [self anotherMethod]; // expected-warning {{use of 'self' after it has been freed with call to [super dealloc]}} - // expected-note@-1 {{use of 'self' after it has been freed with call to [super dealloc]}} + [self anotherMethod]; // expected-warning {{use of 'self' after it has been deallocated}} + // expected-note@-1 {{use of 'self' after it has been deallocated}} [super dealloc]; } @end From 4cc85acd83129da47ce65c6677b158ce5160e250 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Fri, 26 Feb 2016 05:07:00 +0000 Subject: [PATCH 231/742] [Driver] Disable frame pointer elimination by default if target is x86_64-pc-win32-macho. rdar://problem/24470634 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261976 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit f2df5c2972d09c591c92dddbdae2636708ac251f) --- lib/Driver/Tools.cpp | 2 ++ test/Driver/frame-pointer.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index 2106b30ecd1..5ec03e15812 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -2946,6 +2946,8 @@ static bool shouldUseFramePointerForTarget(const ArgList &Args, switch (Triple.getArch()) { case llvm::Triple::x86: return !areOptimizationsEnabled(Args); + case llvm::Triple::x86_64: + return Triple.isOSBinFormatMachO(); case llvm::Triple::arm: case llvm::Triple::thumb: // Windows on ARM builds with FPO disabled to aid fast stack walking diff --git a/test/Driver/frame-pointer.c b/test/Driver/frame-pointer.c index 1d63f2c4292..cec168636c1 100644 --- a/test/Driver/frame-pointer.c +++ b/test/Driver/frame-pointer.c @@ -10,6 +10,7 @@ // RUN: %clang -target x86_64-pc-linux -### -S -O2 %s -o %t.s 2>&1 | FileCheck -check-prefix=CHECK2-64 %s // RUN: %clang -target x86_64-pc-linux -### -S -O3 %s -o %t.s 2>&1 | FileCheck -check-prefix=CHECK3-64 %s // RUN: %clang -target x86_64-pc-linux -### -S -Os %s -o %t.s 2>&1 | FileCheck -check-prefix=CHECKs-64 %s +// RUN: %clang -target x86_64-pc-win32-macho -### -S -O3 %s -o %t.s 2>&1 | FileCheck -check-prefix=CHECK-MACHO-64 %s // Trust the above to get the optimizations right, and just test other targets // that want this by default. @@ -36,3 +37,4 @@ // CHECK2-64-NOT: -mdisable-fp-elim // CHECK3-64-NOT: -mdisable-fp-elim // CHECKs-64-NOT: -mdisable-fp-elim +// CHECK-MACHO-64: -mdisable-fp-elim From d715b30f005954d7586a2b665f6f80fcefe2d9e3 Mon Sep 17 00:00:00 2001 From: "Duncan P. N. Exon Smith" Date: Fri, 26 Feb 2016 19:27:00 +0000 Subject: [PATCH 232/742] SemaCXX: Support templates in availability attributes If the availability context is `FunctionTemplateDecl`, we should look through it to the `FunctionDecl`. This prevents a diagnostic in the following case: class C __attribute__((unavailable)); template void foo(C&) __attribute__((unavailable)); This adds tests for availability in templates in many other cases, but that was the only case that failed before this patch. I added a feature `__has_feature(attribute_availability_in_templates)` so users can test for this. rdar://problem/24561029 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262050 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit a004401660d68b3cd8e272e75c535003b6c02e53) Conflicts: lib/AST/DeclBase.cpp The swift.org version of `getAvailability()` takes an `Optional` that the llvm.org version doesn't take. --- lib/AST/DeclBase.cpp | 4 ++ lib/Lex/PPMacroExpansion.cpp | 1 + test/SemaCXX/attr-unavailable.cpp | 62 ++++++++++++++++++++++++++++++- 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index 6a70730053c..b53e92fdcce 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -471,6 +471,10 @@ checkAvailability(ASTContext &Context, const AvailabilityAttr *A, AvailabilityResult Decl::getAvailability(std::string *Message, Optional Version) const { +AvailabilityResult Decl::getAvailability(std::string *Message) const { + if (auto *FTD = dyn_cast(this)) + return FTD->getTemplatedDecl()->getAvailability(Message, Version); + AvailabilityResult Result = AR_Available; std::string ResultMessage; diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp index 0715002e27b..9f3dc649a49 100644 --- a/lib/Lex/PPMacroExpansion.cpp +++ b/lib/Lex/PPMacroExpansion.cpp @@ -1071,6 +1071,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .Case("attribute_availability_watchos", true) .Case("attribute_availability_swift", true) .Case("attribute_availability_with_strict", true) + .Case("attribute_availability_in_templates", true) .Case("attribute_cf_returns_not_retained", true) .Case("attribute_cf_returns_retained", true) .Case("attribute_cf_returns_on_parameters", true) diff --git a/test/SemaCXX/attr-unavailable.cpp b/test/SemaCXX/attr-unavailable.cpp index 51dc8fe3789..430cb5cdb8c 100644 --- a/test/SemaCXX/attr-unavailable.cpp +++ b/test/SemaCXX/attr-unavailable.cpp @@ -5,7 +5,8 @@ double &foo(double); // expected-note {{candidate}} void foo(...) __attribute__((__unavailable__)); // expected-note {{candidate function}} \ // expected-note{{'foo' has been explicitly marked unavailable here}} -void bar(...) __attribute__((__unavailable__)); // expected-note 2{{explicitly marked unavailable}} +void bar(...) __attribute__((__unavailable__)); // expected-note 2{{explicitly marked unavailable}} \ + // expected-note 2{{candidate function has been explicitly made unavailable}} void test_foo(short* sp) { int &ir = foo(1); @@ -56,3 +57,62 @@ typedef enum UnavailableEnum AnotherUnavailableEnum; // expected-error {{'Unavai __attribute__((unavailable)) UnavailableEnum testUnavailable(UnavailableEnum X) { return X; } + + +// Check that unavailable classes can be used as arguments to unavailable +// function, particularly in template functions. +#if !__has_feature(attribute_availability_in_templates) +#error "Missing __has_feature" +#endif +class __attribute((unavailable)) UnavailableClass; // \ + expected-note 3{{'UnavailableClass' has been explicitly marked unavailable here}} +void unavail_class(UnavailableClass&); // expected-error {{'UnavailableClass' is unavailable}} +void unavail_class_marked(UnavailableClass&) __attribute__((unavailable)); +template void unavail_class(UnavailableClass&); // expected-error {{'UnavailableClass' is unavailable}} +template void unavail_class_marked(UnavailableClass&) __attribute__((unavailable)); +template void templated(T&); +void untemplated(UnavailableClass &UC) { // expected-error {{'UnavailableClass' is unavailable}} + templated(UC); +} +void untemplated_marked(UnavailableClass &UC) __attribute__((unavailable)) { + templated(UC); +} + +template void templated_calls_bar() { bar(); } // \ + // expected-error{{call to unavailable function 'bar'}} +template void templated_calls_bar_arg(T v) { bar(v); } // \ + // expected-error{{call to unavailable function 'bar'}} +template void templated_calls_bar_arg_never_called(T v) { bar(v); } + +template +void unavail_templated_calls_bar() __attribute__((unavailable)) { // \ + expected-note{{candidate function [with T = int] has been explicitly made unavailable}} + bar(5); +} +template +void unavail_templated_calls_bar_arg(T v) __attribute__((unavailable)) { // \ + expected-note{{candidate function [with T = int] has been explicitly made unavailable}} + bar(v); +} + +void calls_templates_which_call_bar() { + templated_calls_bar(); + + templated_calls_bar_arg(5); // \ + expected-note{{in instantiation of function template specialization 'templated_calls_bar_arg' requested here}} + + unavail_templated_calls_bar(); // \ + expected-error{{call to unavailable function 'unavail_templated_calls_bar'}} + + unavail_templated_calls_bar_arg(5); // \ + expected-error{{call to unavailable function 'unavail_templated_calls_bar_arg'}} +} + +template void unavail_templated(T) __attribute__((unavailable)); // \ + expected-note{{candidate function [with T = int] has been explicitly made unavailable}} +void calls_unavail_templated() { + unavail_templated(5); // expected-error{{call to unavailable function 'unavail_templated'}} +} +void unavail_calls_unavail_templated() __attribute__((unavailable)) { + unavail_templated(5); +} From 5eb07d6ed2c0248d041a3ffda9294c38e15422fc Mon Sep 17 00:00:00 2001 From: "Duncan P. N. Exon Smith" Date: Fri, 26 Feb 2016 11:57:47 -0800 Subject: [PATCH 233/742] Embarrassing fixup for cherry-pick of r262050 Fix the build after r262050 was cherry-picked. rdar://problem/24561029 --- lib/AST/DeclBase.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index b53e92fdcce..128a5d08ab8 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -471,7 +471,6 @@ checkAvailability(ASTContext &Context, const AvailabilityAttr *A, AvailabilityResult Decl::getAvailability(std::string *Message, Optional Version) const { -AvailabilityResult Decl::getAvailability(std::string *Message) const { if (auto *FTD = dyn_cast(this)) return FTD->getTemplatedDecl()->getAvailability(Message, Version); From acf51de1e61cd93732f18df661397a5bc1413d89 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Mon, 29 Feb 2016 07:55:51 +0000 Subject: [PATCH 234/742] [index] Use ',' to separate symbol roles when printing. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262205 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Index/IndexSymbol.cpp | 2 +- test/Index/Core/index-source.cpp | 2 +- test/Index/Core/index-source.m | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Index/IndexSymbol.cpp b/lib/Index/IndexSymbol.cpp index 010ccd42a4a..96da7a7989c 100644 --- a/lib/Index/IndexSymbol.cpp +++ b/lib/Index/IndexSymbol.cpp @@ -214,7 +214,7 @@ void index::printSymbolRoles(SymbolRoleSet Roles, raw_ostream &OS) { bool VisitedOnce = false; applyForEachSymbolRole(Roles, [&](SymbolRole Role) { if (VisitedOnce) - OS << '/'; + OS << ','; else VisitedOnce = true; switch (Role) { diff --git a/test/Index/Core/index-source.cpp b/test/Index/Core/index-source.cpp index 75446468266..406f70f3896 100644 --- a/test/Index/Core/index-source.cpp +++ b/test/Index/Core/index-source.cpp @@ -4,6 +4,6 @@ template class TemplCls { // CHECK: [[@LINE-1]]:7 | c++-class/C++ | TemplCls | c:@ST>1#T@TemplCls | | Def | rel: 0 TemplCls(int x); - // CHECK: [[@LINE-1]]:3 | constructor/C++ | TemplCls | c:@ST>1#T@TemplCls@F@TemplCls#I# | | Decl/RelChild | rel: 1 + // CHECK: [[@LINE-1]]:3 | constructor/C++ | TemplCls | c:@ST>1#T@TemplCls@F@TemplCls#I# | | Decl,RelChild | rel: 1 // CHECK-NEXT: RelChild | TemplCls | c:@ST>1#T@TemplCls }; diff --git a/test/Index/Core/index-source.m b/test/Index/Core/index-source.m index 0fdae7ae5f9..6bff76ed27c 100644 --- a/test/Index/Core/index-source.m +++ b/test/Index/Core/index-source.m @@ -3,6 +3,6 @@ @interface Base // CHECK: [[@LINE-1]]:12 | objc-class/ObjC | Base | c:objc(cs)Base | _OBJC_CLASS_$_Base | Decl | rel: 0 -(void)meth; -// CHECK: [[@LINE-1]]:1 | objc-instance-method/ObjC | meth | c:objc(cs)Base(im)meth | -[Base meth] | Decl/Dyn/RelChild | rel: 1 +// CHECK: [[@LINE-1]]:1 | objc-instance-method/ObjC | meth | c:objc(cs)Base(im)meth | -[Base meth] | Decl,Dyn,RelChild | rel: 1 // CHECK-NEXT: RelChild | Base | c:objc(cs)Base @end From a29bcd562a7d4598d02a0387bd8077f2b0ccd547 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Mon, 29 Feb 2016 07:55:55 +0000 Subject: [PATCH 235/742] [AST/RecursiveASTVisitor] Correction so that dataTraverseStmtPost will get called after the statement has been visited. Fixes the indexing client of this. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262206 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/RecursiveASTVisitor.h | 29 +++++++++++++++++-------- test/Index/Core/index-source.m | 10 +++++++++ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h index 42e67517a61..27cb7bd2b17 100644 --- a/include/clang/AST/RecursiveASTVisitor.h +++ b/include/clang/AST/RecursiveASTVisitor.h @@ -139,7 +139,9 @@ template class RecursiveASTVisitor { /// Parameters involving this type are used to implement data /// recursion over Stmts and Exprs within this class, and should /// typically not be explicitly specified by derived classes. - typedef SmallVectorImpl DataRecursionQueue; + /// The bool bit indicates whether the statement has been traversed or not. + typedef SmallVectorImpl> + DataRecursionQueue; /// \brief Return a reference to the derived class. Derived &getDerived() { return *static_cast(this); } @@ -558,23 +560,32 @@ bool RecursiveASTVisitor::TraverseStmt(Stmt *S, return true; if (Queue) { - Queue->push_back(S); + Queue->push_back({S, false}); return true; } - SmallVector LocalQueue; - LocalQueue.push_back(S); + SmallVector, 8> LocalQueue; + LocalQueue.push_back({S, false}); while (!LocalQueue.empty()) { - Stmt *CurrS = LocalQueue.pop_back_val(); + auto &CurrSAndVisited = LocalQueue.back(); + Stmt *CurrS = CurrSAndVisited.getPointer(); + bool Visited = CurrSAndVisited.getInt(); + if (Visited) { + LocalQueue.pop_back(); + TRY_TO(dataTraverseStmtPost(CurrS)); + continue; + } - size_t N = LocalQueue.size(); if (getDerived().dataTraverseStmtPre(CurrS)) { + CurrSAndVisited.setInt(true); + size_t N = LocalQueue.size(); TRY_TO(dataTraverseNode(CurrS, &LocalQueue)); - TRY_TO(dataTraverseStmtPost(CurrS)); + // Process new children in the order they were added. + std::reverse(LocalQueue.begin() + N, LocalQueue.end()); + } else { + LocalQueue.pop_back(); } - // Process new children in the order they were added. - std::reverse(LocalQueue.begin() + N, LocalQueue.end()); } return true; diff --git a/test/Index/Core/index-source.m b/test/Index/Core/index-source.m index 6bff76ed27c..3307ec54393 100644 --- a/test/Index/Core/index-source.m +++ b/test/Index/Core/index-source.m @@ -6,3 +6,13 @@ -(void)meth; // CHECK: [[@LINE-1]]:1 | objc-instance-method/ObjC | meth | c:objc(cs)Base(im)meth | -[Base meth] | Decl,Dyn,RelChild | rel: 1 // CHECK-NEXT: RelChild | Base | c:objc(cs)Base @end + +void foo(); +// CHECK: [[@LINE+1]]:6 | function/C | goo | c:@F@goo | _goo | Def | rel: 0 +void goo(Base *b) { + // CHECK: [[@LINE+1]]:3 | function/C | foo | c:@F@foo | _foo | Ref,Call | rel: 0 + foo(); + // CHECK: [[@LINE+2]]:6 | objc-instance-method/ObjC | meth | c:objc(cs)Base(im)meth | -[Base meth] | Ref,Call,Dyn,RelRec | rel: 1 + // CHECK-NEXT: RelRec | Base | c:objc(cs)Base + [b meth]; +} From 1f38fe24a89b91daf54041ca07fd00c4f0500d45 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Mon, 29 Feb 2016 07:56:00 +0000 Subject: [PATCH 236/742] [index] Add a caller relation for a call reference. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262207 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Index/IndexSymbol.h | 3 ++- lib/Index/IndexBody.cpp | 45 ++++++++++++++++++++----------- lib/Index/IndexSymbol.cpp | 2 ++ test/Index/Core/index-source.m | 6 +++-- 4 files changed, 37 insertions(+), 19 deletions(-) diff --git a/include/clang/Index/IndexSymbol.h b/include/clang/Index/IndexSymbol.h index 506540b0e6e..484edda696f 100644 --- a/include/clang/Index/IndexSymbol.h +++ b/include/clang/Index/IndexSymbol.h @@ -88,8 +88,9 @@ enum class SymbolRole : uint16_t { RelationBaseOf = 1 << 10, RelationOverrideOf = 1 << 11, RelationReceivedBy = 1 << 12, + RelationCalledBy = 1 << 13, }; -static const unsigned SymbolRoleBitNum = 13; +static const unsigned SymbolRoleBitNum = 14; typedef unsigned SymbolRoleSet; /// Represents a relation to another symbol for a symbol occurrence. diff --git a/lib/Index/IndexBody.cpp b/lib/Index/IndexBody.cpp index f7164453db6..fda8388a324 100644 --- a/lib/Index/IndexBody.cpp +++ b/lib/Index/IndexBody.cpp @@ -88,7 +88,7 @@ class BodyIndexer : public RecursiveASTVisitor { } else if (auto CE = dyn_cast(Parent)) { if (CE->getCallee()->IgnoreParenCasts() == E) { - Roles |= (unsigned)SymbolRole::Call; + addCallRole(Roles, Relations); if (auto *ME = dyn_cast(E)) { if (auto *CXXMD = dyn_cast_or_null(ME->getMemberDecl())) if (CXXMD->isVirtual() && !ME->hasQualifier()) { @@ -120,6 +120,15 @@ class BodyIndexer : public RecursiveASTVisitor { return Roles; } + void addCallRole(SymbolRoleSet &Roles, + SmallVectorImpl &Relations) { + Roles |= (unsigned)SymbolRole::Call; + if (auto *FD = dyn_cast(ParentDC)) + Relations.emplace_back((unsigned)SymbolRole::RelationCalledBy, FD); + else if (auto *MD = dyn_cast(ParentDC)) + Relations.emplace_back((unsigned)SymbolRole::RelationCalledBy, MD); + } + bool VisitDeclRefExpr(DeclRefExpr *E) { SmallVector Relations; SymbolRoleSet Roles = getRolesForRef(E, Relations); @@ -169,11 +178,12 @@ class BodyIndexer : public RecursiveASTVisitor { }; if (ObjCMethodDecl *MD = E->getMethodDecl()) { - SymbolRoleSet Roles = (unsigned)SymbolRole::Call; + SymbolRoleSet Roles{}; + SmallVector Relations; + addCallRole(Roles, Relations); if (E->isImplicit()) Roles |= (unsigned)SymbolRole::Implicit; - SmallVector Relations; if (isDynamic(E)) { Roles |= (unsigned)SymbolRole::Dynamic; if (auto *RecD = E->getReceiverInterface()) @@ -206,39 +216,42 @@ class BodyIndexer : public RecursiveASTVisitor { Parent, ParentDC, SymbolRoleSet(), {}, E); } + bool passObjCLiteralMethodCall(const ObjCMethodDecl *MD, const Expr *E) { + SymbolRoleSet Roles{}; + SmallVector Relations; + addCallRole(Roles, Relations); + Roles |= (unsigned)SymbolRole::Implicit; + return IndexCtx.handleReference(MD, E->getLocStart(), + Parent, ParentDC, Roles, Relations, E); + } + bool VisitObjCBoxedExpr(ObjCBoxedExpr *E) { if (ObjCMethodDecl *MD = E->getBoxingMethod()) { - SymbolRoleSet Roles = (unsigned)SymbolRole::Call; - Roles |= (unsigned)SymbolRole::Implicit; - return IndexCtx.handleReference(MD, E->getLocStart(), - Parent, ParentDC, Roles, {}, E); + return passObjCLiteralMethodCall(MD, E); } return true; } bool VisitObjCDictionaryLiteral(ObjCDictionaryLiteral *E) { if (ObjCMethodDecl *MD = E->getDictWithObjectsMethod()) { - SymbolRoleSet Roles = (unsigned)SymbolRole::Call; - Roles |= (unsigned)SymbolRole::Implicit; - return IndexCtx.handleReference(MD, E->getLocStart(), - Parent, ParentDC, Roles, {}, E); + return passObjCLiteralMethodCall(MD, E); } return true; } bool VisitObjCArrayLiteral(ObjCArrayLiteral *E) { if (ObjCMethodDecl *MD = E->getArrayWithObjectsMethod()) { - SymbolRoleSet Roles = (unsigned)SymbolRole::Call; - Roles |= (unsigned)SymbolRole::Implicit; - return IndexCtx.handleReference(MD, E->getLocStart(), - Parent, ParentDC, Roles, {}, E); + return passObjCLiteralMethodCall(MD, E); } return true; } bool VisitCXXConstructExpr(CXXConstructExpr *E) { + SymbolRoleSet Roles{}; + SmallVector Relations; + addCallRole(Roles, Relations); return IndexCtx.handleReference(E->getConstructor(), E->getLocation(), - Parent, ParentDC, (unsigned)SymbolRole::Call, {}, E); + Parent, ParentDC, Roles, Relations, E); } bool TraverseCXXOperatorCallExpr(CXXOperatorCallExpr *E, diff --git a/lib/Index/IndexSymbol.cpp b/lib/Index/IndexSymbol.cpp index 96da7a7989c..39812c4fe4f 100644 --- a/lib/Index/IndexSymbol.cpp +++ b/lib/Index/IndexSymbol.cpp @@ -206,6 +206,7 @@ void index::applyForEachSymbolRole(SymbolRoleSet Roles, APPLY_FOR_ROLE(RelationBaseOf); APPLY_FOR_ROLE(RelationOverrideOf); APPLY_FOR_ROLE(RelationReceivedBy); + APPLY_FOR_ROLE(RelationCalledBy); #undef APPLY_FOR_ROLE } @@ -231,6 +232,7 @@ void index::printSymbolRoles(SymbolRoleSet Roles, raw_ostream &OS) { case SymbolRole::RelationBaseOf: OS << "RelBase"; break; case SymbolRole::RelationOverrideOf: OS << "RelOver"; break; case SymbolRole::RelationReceivedBy: OS << "RelRec"; break; + case SymbolRole::RelationCalledBy: OS << "RelCall"; break; } }); } diff --git a/test/Index/Core/index-source.m b/test/Index/Core/index-source.m index 3307ec54393..c2604f67545 100644 --- a/test/Index/Core/index-source.m +++ b/test/Index/Core/index-source.m @@ -10,9 +10,11 @@ -(void)meth; void foo(); // CHECK: [[@LINE+1]]:6 | function/C | goo | c:@F@goo | _goo | Def | rel: 0 void goo(Base *b) { - // CHECK: [[@LINE+1]]:3 | function/C | foo | c:@F@foo | _foo | Ref,Call | rel: 0 + // CHECK: [[@LINE+2]]:3 | function/C | foo | c:@F@foo | _foo | Ref,Call,RelCall | rel: 1 + // CHECK-NEXT: RelCall | goo | c:@F@goo foo(); - // CHECK: [[@LINE+2]]:6 | objc-instance-method/ObjC | meth | c:objc(cs)Base(im)meth | -[Base meth] | Ref,Call,Dyn,RelRec | rel: 1 + // CHECK: [[@LINE+3]]:6 | objc-instance-method/ObjC | meth | c:objc(cs)Base(im)meth | -[Base meth] | Ref,Call,Dyn,RelRec,RelCall | rel: 2 + // CHECK-NEXT: RelCall | goo | c:@F@goo // CHECK-NEXT: RelRec | Base | c:objc(cs)Base [b meth]; } From fe497517bd8b7f8f5baa57dadba65acfab222c8e Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Mon, 29 Feb 2016 07:56:07 +0000 Subject: [PATCH 237/742] [index] Print and test module import references. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262208 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Index/IndexSymbol.cpp | 3 +++ lib/Index/IndexingContext.cpp | 9 +++++++-- test/Index/Core/Inputs/module/ModA.h | 2 ++ .../Index/Core/Inputs/module/module.modulemap | 1 + test/Index/Core/index-with-module.m | 12 +++++++++++ tools/c-index-test/core_main.cpp | 20 +++++++++++++++++++ 6 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 test/Index/Core/Inputs/module/ModA.h create mode 100644 test/Index/Core/Inputs/module/module.modulemap create mode 100644 test/Index/Core/index-with-module.m diff --git a/lib/Index/IndexSymbol.cpp b/lib/Index/IndexSymbol.cpp index 39812c4fe4f..62e2facde19 100644 --- a/lib/Index/IndexSymbol.cpp +++ b/lib/Index/IndexSymbol.cpp @@ -53,6 +53,9 @@ SymbolInfo index::getSymbolInfo(const Decl *D) { } else { switch (D->getKind()) { + case Decl::Import: + Info.Kind = SymbolKind::Module; + break; case Decl::Typedef: Info.Kind = SymbolKind::Typedef; break; case Decl::Function: diff --git a/lib/Index/IndexingContext.cpp b/lib/Index/IndexingContext.cpp index 9ac22f85e1d..1645a9ab0bd 100644 --- a/lib/Index/IndexingContext.cpp +++ b/lib/Index/IndexingContext.cpp @@ -58,7 +58,12 @@ bool IndexingContext::handleReference(const NamedDecl *D, SourceLocation Loc, } bool IndexingContext::importedModule(const ImportDecl *ImportD) { - SourceLocation Loc = ImportD->getLocation(); + SourceLocation Loc; + auto IdLocs = ImportD->getIdentifierLocs(); + if (!IdLocs.empty()) + Loc = IdLocs.front(); + else + Loc = ImportD->getLocation(); SourceManager &SM = Ctx->getSourceManager(); Loc = SM.getFileLoc(Loc); if (Loc.isInvalid()) @@ -85,7 +90,7 @@ bool IndexingContext::importedModule(const ImportDecl *ImportD) { } } - SymbolRoleSet Roles{}; + SymbolRoleSet Roles = (unsigned)SymbolRole::Reference; if (ImportD->isImplicit()) Roles |= (unsigned)SymbolRole::Implicit; diff --git a/test/Index/Core/Inputs/module/ModA.h b/test/Index/Core/Inputs/module/ModA.h new file mode 100644 index 00000000000..081d86cc451 --- /dev/null +++ b/test/Index/Core/Inputs/module/ModA.h @@ -0,0 +1,2 @@ + +void ModA_func(void); diff --git a/test/Index/Core/Inputs/module/module.modulemap b/test/Index/Core/Inputs/module/module.modulemap new file mode 100644 index 00000000000..a132562eafd --- /dev/null +++ b/test/Index/Core/Inputs/module/module.modulemap @@ -0,0 +1 @@ +module ModA { header "ModA.h" export * } diff --git a/test/Index/Core/index-with-module.m b/test/Index/Core/index-with-module.m new file mode 100644 index 00000000000..646a48a2c93 --- /dev/null +++ b/test/Index/Core/index-with-module.m @@ -0,0 +1,12 @@ +// RUN: rm -rf %t.mcp +// RUN: c-index-test core -print-source-symbols -- %s -I %S/Inputs/module -fmodules -fmodules-cache-path=%t.mcp | FileCheck %s + +// CHECK: [[@LINE+1]]:9 | module/C | ModA | Ref | +@import ModA; +// CHECK: [[@LINE+1]]:1 | module/C | ModA | Ref,Impl | +#include "ModA.h" + +void foo() { + // CHECK: [[@LINE+1]]:3 | function/C | ModA_func | c:@F@ModA_func | {{.*}} | Ref,Call,RelCall | rel: 1 + ModA_func(); +} \ No newline at end of file diff --git a/tools/c-index-test/core_main.cpp b/tools/c-index-test/core_main.cpp index e72b9f93efd..1881e31e207 100644 --- a/tools/c-index-test/core_main.cpp +++ b/tools/c-index-test/core_main.cpp @@ -107,6 +107,26 @@ class PrintIndexDataConsumer : public IndexDataConsumer { return true; } + + bool handleModuleOccurence(const ImportDecl *ImportD, SymbolRoleSet Roles, + FileID FID, unsigned Offset) override { + ASTContext &Ctx = ImportD->getASTContext(); + SourceManager &SM = Ctx.getSourceManager(); + + unsigned Line = SM.getLineNumber(FID, Offset); + unsigned Col = SM.getColumnNumber(FID, Offset); + OS << Line << ':' << Col << " | "; + + printSymbolInfo(getSymbolInfo(ImportD), OS); + OS << " | "; + + OS << ImportD->getImportedModule()->getFullModuleName() << " | "; + + printSymbolRoles(Roles, OS); + OS << " |\n"; + + return true; + } }; } // anonymous namespace From 6588f2ce3c54661b5e6a820535f5d22689544ccb Mon Sep 17 00:00:00 2001 From: Alexey Samsonov Date: Sat, 13 Feb 2016 01:02:59 +0000 Subject: [PATCH 238/742] Disable two tests that use a lot of stack under ASan. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260779 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/Index/index-many-call-ops.cpp | 4 ++-- test/Index/index-many-logical-ops.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/Index/index-many-call-ops.cpp b/test/Index/index-many-call-ops.cpp index 7644697d4e7..b46029cbf8c 100644 --- a/test/Index/index-many-call-ops.cpp +++ b/test/Index/index-many-call-ops.cpp @@ -4,8 +4,8 @@ // Check that we don't get stack overflow trying to index a huge number of // call operators. -// UBSan increses stack usage. -// REQUIRES: not_ubsan +// ASan and UBSan increase stack usage. +// REQUIRES: not_asan, not_ubsan struct S { S &operator()(); diff --git a/test/Index/index-many-logical-ops.c b/test/Index/index-many-logical-ops.c index 0fd4e75236f..fd994a23ac6 100644 --- a/test/Index/index-many-logical-ops.c +++ b/test/Index/index-many-logical-ops.c @@ -4,8 +4,8 @@ // Check that we don't get stack overflow trying to index a huge number of // logical operators. -// UBSan increses stack usage. -// REQUIRES: not_ubsan +// ASan and UBSan increase stack usage. +// REQUIRES: not_asan, not_ubsan // CHECK: [indexDeclaration]: kind: function | name: foo int foo(int x) { From c4c17432cf262b4518340f62d7db4819c8a1edf3 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Tue, 1 Mar 2016 02:46:32 +0000 Subject: [PATCH 239/742] [index] Fix issue where data visitation was disabled with C++ operator call expressions, during indexing. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262290 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Index/IndexBody.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Index/IndexBody.cpp b/lib/Index/IndexBody.cpp index fda8388a324..74e082a7cae 100644 --- a/lib/Index/IndexBody.cpp +++ b/lib/Index/IndexBody.cpp @@ -258,7 +258,7 @@ class BodyIndexer : public RecursiveASTVisitor { DataRecursionQueue *Q = nullptr) { if (E->getOperatorLoc().isInvalid()) return true; // implicit. - return base::TraverseCXXOperatorCallExpr(E); + return base::TraverseCXXOperatorCallExpr(E, Q); } bool VisitDeclStmt(DeclStmt *S) { @@ -325,7 +325,7 @@ class BodyIndexer : public RecursiveASTVisitor { auto visitForm = [&](InitListExpr *Form) { for (Stmt *SubStmt : Form->children()) { - if (!TraverseStmt(SubStmt)) + if (!TraverseStmt(SubStmt, Q)) return false; } return true; From d657fba3476fcc3954eee475d1a5a908402bde89 Mon Sep 17 00:00:00 2001 From: NAKAMURA Takumi Date: Fri, 26 Feb 2016 03:15:13 +0000 Subject: [PATCH 240/742] Checkers/CheckObjCDealloc.cpp: Prune "\param". [-Wdocumentation] git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261963 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit aa1a4b66c0b8f8352c42546fdd77a740df7161c3) --- lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index 28fae7ca1b3..b427a9a30c4 100644 --- a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -635,7 +635,7 @@ bool ObjCDeallocChecker::isSuperDeallocMessage( return M.getSelector() == DeallocSel; } -/// Returns the ObjCImplDecl containing the method declaration in \param LCtx. +/// Returns the ObjCImplDecl containing the method declaration in LCtx. const ObjCImplDecl * ObjCDeallocChecker::getContainingObjCImpl(const LocationContext *LCtx) const { auto *MD = cast(LCtx->getDecl()); From 7197d580cc35755816786bba34733604618155f5 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Fri, 26 Feb 2016 03:41:31 +0000 Subject: [PATCH 241/742] [analyzer] Prune some incorrect \param doc comment annotations. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261970 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit b22bef46a52062cfd49eb4591c24114ebfb54840) --- .../Checkers/CheckObjCDealloc.cpp | 24 +++++++++---------- .../Checkers/ObjCSuperDeallocChecker.cpp | 6 ++--- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index b427a9a30c4..42999e2fd30 100644 --- a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -506,7 +506,7 @@ void ObjCDeallocChecker::diagnoseMissingReleases(CheckerContext &C) const { assert(!LCtx->inTopFrame() || State->get().isEmpty()); } -/// Emits a warning if the current context is -dealloc and \param ReleasedValue +/// Emits a warning if the current context is -dealloc and ReleasedValue /// must not be directly released in a -dealloc. Returns true if a diagnostic /// was emitted. bool ObjCDeallocChecker::diagnoseExtraRelease(SymbolRef ReleasedValue, @@ -626,7 +626,7 @@ void ObjCDeallocChecker::initIdentifierInfoAndSelectors( ReleaseSel = Ctx.Selectors.getSelector(0, &ReleaseII); } -/// Returns true if \param M is a call to '[super dealloc]'. +/// Returns true if M is a call to '[super dealloc]'. bool ObjCDeallocChecker::isSuperDeallocMessage( const ObjCMethodCall &M) const { if (M.getOriginExpr()->getReceiverKind() != ObjCMessageExpr::SuperInstance) @@ -642,7 +642,7 @@ ObjCDeallocChecker::getContainingObjCImpl(const LocationContext *LCtx) const { return cast(MD->getDeclContext()); } -/// Returns the property that shadowed by \param PropImpl if one exists and +/// Returns the property that shadowed by PropImpl if one exists and /// nullptr otherwise. const ObjCPropertyDecl *ObjCDeallocChecker::findShadowedPropertyDecl( const ObjCPropertyImplDecl *PropImpl) const { @@ -674,8 +674,8 @@ const ObjCPropertyDecl *ObjCDeallocChecker::findShadowedPropertyDecl( return nullptr; } -/// Remove the \param Value requiring a release from the tracked set for -/// \param Instance and return the resultant state. +/// Remove the Value requiring a release from the tracked set for +/// Instance and return the resultant state. ProgramStateRef ObjCDeallocChecker::removeValueRequiringRelease( ProgramStateRef State, SymbolRef Instance, SymbolRef Value) const { assert(Instance); @@ -729,7 +729,7 @@ ReleaseRequirement ObjCDeallocChecker::getDeallocReleaseRequirement( llvm_unreachable("Unrecognized setter kind"); } -/// Returns the released value if \param M is a call to -release. Returns +/// Returns the released value if M is a call to -release. Returns /// nullptr otherwise. SymbolRef ObjCDeallocChecker::getValueExplicitlyReleased(const ObjCMethodCall &M, @@ -740,7 +740,7 @@ ObjCDeallocChecker::getValueExplicitlyReleased(const ObjCMethodCall &M, return M.getReceiverSVal().getAsSymbol(); } -/// Returns the released value if \param M is a call a setter that releases +/// Returns the released value if M is a call a setter that releases /// and nils out its underlying instance variable. SymbolRef ObjCDeallocChecker::getValueReleasedByNillingOut(const ObjCMethodCall &M, @@ -779,15 +779,15 @@ ObjCDeallocChecker::getValueReleasedByNillingOut(const ObjCMethodCall &M, } /// Returns true if the current context is a call to -dealloc and false -/// otherwise. If true, it also sets \param SelfValOut to the value of +/// otherwise. If true, it also sets SelfValOut to the value of /// 'self'. bool ObjCDeallocChecker::isInInstanceDealloc(const CheckerContext &C, SVal &SelfValOut) const { return isInInstanceDealloc(C, C.getLocationContext(), SelfValOut); } -/// Returns true if \param LCtx is a call to -dealloc and false -/// otherwise. If true, it also sets \param SelfValOut to the value of +/// Returns true if LCtx is a call to -dealloc and false +/// otherwise. If true, it also sets SelfValOut to the value of /// 'self'. bool ObjCDeallocChecker::isInInstanceDealloc(const CheckerContext &C, const LocationContext *LCtx, @@ -805,7 +805,7 @@ bool ObjCDeallocChecker::isInInstanceDealloc(const CheckerContext &C, } /// Returns true if there is a call to -dealloc anywhere on the stack and false -/// otherwise. If true, it also sets \param InstanceValOut to the value of +/// otherwise. If true, it also sets InstanceValOut to the value of /// 'self' in the frame for -dealloc. bool ObjCDeallocChecker::instanceDeallocIsOnStack(const CheckerContext &C, SVal &InstanceValOut) const { @@ -821,7 +821,7 @@ bool ObjCDeallocChecker::instanceDeallocIsOnStack(const CheckerContext &C, return false; } -/// Returns true if the \param ID is a class in which which is known to have +/// Returns true if the ID is a class in which which is known to have /// a separate teardown lifecycle. In this case, -dealloc warnings /// about missing releases should be suppressed. bool ObjCDeallocChecker::classHasSeparateTeardown( diff --git a/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp index 9c1d69d2fe2..8618eeb0dfe 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp @@ -173,8 +173,8 @@ void ObjCSuperDeallocChecker::checkLocation(SVal L, bool IsLoad, const Stmt *S, reportUseAfterDealloc(BaseSym, Desc, S, C); } -/// Report a use-after-dealloc on \param Sym. If not empty, -/// \param Desc will be used to describe the error; otherwise, +/// Report a use-after-dealloc on Sym. If not empty, +/// Desc will be used to describe the error; otherwise, /// a default warning will be used. void ObjCSuperDeallocChecker::reportUseAfterDealloc(SymbolRef Sym, StringRef Desc, @@ -199,7 +199,7 @@ void ObjCSuperDeallocChecker::reportUseAfterDealloc(SymbolRef Sym, C.emitReport(std::move(BR)); } -/// Diagnose if any of the arguments to \param CE have already been +/// Diagnose if any of the arguments to CE have already been /// dealloc'd. void ObjCSuperDeallocChecker::diagnoseCallArguments(const CallEvent &CE, CheckerContext &C) const { From 8dc10e471a7fe626aff3ff788b2e645962cf9f9d Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Mon, 29 Feb 2016 21:44:08 +0000 Subject: [PATCH 242/742] [analyzer] Don't treat calls to system headers as escaping in CheckObjCDealloc. This prevents false negatives when a -dealloc method, for example, removes itself as as an observer with [[NSNotificationCenter defaultCenter] removeObserver:self]. It is unlikely that passing 'self' to a system header method will release 'self''s instance variables, so this is unlikely to produce false positives. A challenge here is that while CheckObjCDealloc no longer treats these calls as escaping, the rest of the analyzer still does. In particular, this means that loads from the same instance variable before and after a call to a system header will result in different symbols being loaded by the region store. To account for this, the checker now treats different ivar symbols with the same instance and ivar decl as the same for the purpose of release checking and more eagerly removes a release requirement when an instance variable is assumed to be nil. This was not needed before because when an ivar escaped its release requirement was always removed -- now the requirement is not removed for calls to system headers. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262261 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 2d53cf1e66d20cc73a1d4f7bf28016f8ab497959) --- .../Checkers/CheckObjCDealloc.cpp | 117 ++++++++++++++---- test/Analysis/DeallocMissingRelease.m | 62 +++++++--- ...system-header-simulator-for-objc-dealloc.h | 29 +++++ 3 files changed, 170 insertions(+), 38 deletions(-) create mode 100644 test/Analysis/Inputs/system-header-simulator-for-objc-dealloc.h diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index 42999e2fd30..f72a1aeb8a9 100644 --- a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -93,6 +93,7 @@ class ObjCDeallocChecker : public Checker, check::PreObjCMessage, check::PostObjCMessage, check::BeginFunction, check::EndFunction, + eval::Assume, check::PointerEscape, check::PreStmt> { @@ -111,6 +112,9 @@ class ObjCDeallocChecker void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; + ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond, + bool Assumption) const; + ProgramStateRef checkPointerEscape(ProgramStateRef State, const InvalidatedSymbols &Escaped, const CallEvent *Call, @@ -129,6 +133,7 @@ class ObjCDeallocChecker SymbolRef getValueReleasedByNillingOut(const ObjCMethodCall &M, CheckerContext &C) const; + const ObjCIvarRegion *getIvarRegionForIvarSymbol(SymbolRef IvarSym) const; SymbolRef getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym) const; ReleaseRequirement @@ -284,20 +289,31 @@ void ObjCDeallocChecker::checkBeginFunction( } } +/// Given a symbol for an ivar, return the ivar region it was loaded from. +/// Returns nullptr if the instance symbol cannot be found. +const ObjCIvarRegion * +ObjCDeallocChecker::getIvarRegionForIvarSymbol(SymbolRef IvarSym) const { + const MemRegion *RegionLoadedFrom = nullptr; + if (auto *DerivedSym = dyn_cast(IvarSym)) + RegionLoadedFrom = DerivedSym->getRegion(); + else if (auto *RegionSym = dyn_cast(IvarSym)) + RegionLoadedFrom = RegionSym->getRegion(); + else + return nullptr; + + return dyn_cast(RegionLoadedFrom); +} + /// Given a symbol for an ivar, return a symbol for the instance containing /// the ivar. Returns nullptr if the instance symbol cannot be found. SymbolRef ObjCDeallocChecker::getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym) const { - if (auto *SRV = dyn_cast(IvarSym)) { - const TypedValueRegion *TVR = SRV->getRegion(); - const ObjCIvarRegion *IvarRegion = dyn_cast_or_null(TVR); - if (!IvarRegion) - return nullptr; - return IvarRegion->getSymbolicBase()->getSymbol(); - } + const ObjCIvarRegion *IvarRegion = getIvarRegionForIvarSymbol(IvarSym); + if (!IvarRegion) + return nullptr; - return nullptr; + return IvarRegion->getSymbolicBase()->getSymbol(); } /// If we are in -dealloc or -dealloc is on the stack, handle the call if it is @@ -362,11 +378,58 @@ void ObjCDeallocChecker::checkPreStmt( diagnoseMissingReleases(C); } +/// When a symbol is assumed to be nil, remove it from the set of symbols +/// require to be nil. +ProgramStateRef ObjCDeallocChecker::evalAssume(ProgramStateRef State, SVal Cond, + bool Assumption) const { + if (State->get().isEmpty()) + return State; + + auto *CondBSE = dyn_cast_or_null(Cond.getAsSymExpr()); + if (!CondBSE) + return State; + + BinaryOperator::Opcode OpCode = CondBSE->getOpcode(); + if (Assumption) { + if (OpCode != BO_EQ) + return State; + } else { + if (OpCode != BO_NE) + return State; + } + + SymbolRef NullSymbol = nullptr; + if (auto *SIE = dyn_cast(CondBSE)) { + const llvm::APInt &RHS = SIE->getRHS(); + if (RHS != 0) + return State; + NullSymbol = SIE->getLHS(); + } else if (auto *SIE = dyn_cast(CondBSE)) { + const llvm::APInt &LHS = SIE->getLHS(); + if (LHS != 0) + return State; + NullSymbol = SIE->getRHS(); + } else { + return State; + } + + SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(NullSymbol); + if (!InstanceSymbol) + return State; + + State = removeValueRequiringRelease(State, InstanceSymbol, NullSymbol); + + return State; +} + /// If a symbol escapes conservatively assume unseen code released it. ProgramStateRef ObjCDeallocChecker::checkPointerEscape( ProgramStateRef State, const InvalidatedSymbols &Escaped, const CallEvent *Call, PointerEscapeKind Kind) const { + if (State->get().isEmpty()) + return State; + // Don't treat calls to '[super dealloc]' as escaping for the purposes // of this checker. Because the checker diagnoses missing releases in the // post-message handler for '[super dealloc], escaping here would cause @@ -376,7 +439,17 @@ ProgramStateRef ObjCDeallocChecker::checkPointerEscape( return State; for (const auto &Sym : Escaped) { - State = State->remove(Sym); + if (!Call || (Call && !Call->isInSystemHeader())) { + // If Sym is a symbol for an object with instance variables that + // must be released, remove these obligations when the object escapes + // unless via a call to a system function. System functions are + // very unlikely to release instance variables on objects passed to them, + // and are frequently called on 'self' in -dealloc (e.g., to remove + // observers) -- we want to avoid false negatives from escaping on + // them. + State = State->remove(Sym); + } + SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(Sym); if (!InstanceSymbol) @@ -424,9 +497,10 @@ void ObjCDeallocChecker::diagnoseMissingReleases(CheckerContext &C) const { if (SelfRegion != IvarRegion->getSuperRegion()) continue; + const ObjCIvarDecl *IvarDecl = IvarRegion->getDecl(); // Prevent an inlined call to -dealloc in a super class from warning // about the values the subclass's -dealloc should release. - if (IvarRegion->getDecl()->getContainingInterface() != + if (IvarDecl->getContainingInterface() != cast(LCtx->getDecl())->getClassInterface()) continue; @@ -452,7 +526,6 @@ void ObjCDeallocChecker::diagnoseMissingReleases(CheckerContext &C) const { std::string Buf; llvm::raw_string_ostream OS(Buf); - const ObjCIvarDecl *IvarDecl = IvarRegion->getDecl(); const ObjCInterfaceDecl *Interface = IvarDecl->getContainingInterface(); // If the class is known to have a lifecycle with teardown that is // separate from -dealloc, do not warn about missing releases. We @@ -521,15 +594,7 @@ bool ObjCDeallocChecker::diagnoseExtraRelease(SymbolRef ReleasedValue, // values that must not be released in the state. This is because even if // these values escape, it is still an error under the rules of MRR to // release them in -dealloc. - const MemRegion *RegionLoadedFrom = nullptr; - if (auto *DerivedSym = dyn_cast(ReleasedValue)) - RegionLoadedFrom = DerivedSym->getRegion(); - else if (auto *RegionSym = dyn_cast(ReleasedValue)) - RegionLoadedFrom = RegionSym->getRegion(); - else - return false; - - auto *ReleasedIvar = dyn_cast(RegionLoadedFrom); + auto *ReleasedIvar = getIvarRegionForIvarSymbol(ReleasedValue); if (!ReleasedIvar) return false; @@ -680,6 +745,9 @@ ProgramStateRef ObjCDeallocChecker::removeValueRequiringRelease( ProgramStateRef State, SymbolRef Instance, SymbolRef Value) const { assert(Instance); assert(Value); + const ObjCIvarRegion *RemovedRegion = getIvarRegionForIvarSymbol(Value); + if (!RemovedRegion) + return State; const SymbolSet *Unreleased = State->get(Instance); if (!Unreleased) @@ -687,7 +755,14 @@ ProgramStateRef ObjCDeallocChecker::removeValueRequiringRelease( // Mark the value as no longer requiring a release. SymbolSet::Factory &F = State->getStateManager().get_context(); - SymbolSet NewUnreleased = F.remove(*Unreleased, Value); + SymbolSet NewUnreleased = *Unreleased; + for (auto &Sym : *Unreleased) { + const ObjCIvarRegion *UnreleasedRegion = getIvarRegionForIvarSymbol(Sym); + assert(UnreleasedRegion); + if (RemovedRegion->getDecl() == UnreleasedRegion->getDecl()) { + NewUnreleased = F.remove(NewUnreleased, Sym); + } + } if (NewUnreleased.isEmpty()) { return State->remove(Instance); diff --git a/test/Analysis/DeallocMissingRelease.m b/test/Analysis/DeallocMissingRelease.m index c7769db3d5f..bb67e436b2b 100644 --- a/test/Analysis/DeallocMissingRelease.m +++ b/test/Analysis/DeallocMissingRelease.m @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -analyze -analyzer-checker=alpha.osx.cocoa.Dealloc -fblocks -verify %s // RUN: %clang_cc1 -analyze -analyzer-checker=alpha.osx.cocoa.Dealloc -fblocks -triple x86_64-apple-darwin10 -fobjc-arc -fobjc-runtime-has-weak -verify %s -#define nil ((id)0) +#include "Inputs/system-header-simulator-for-objc-dealloc.h" #define NON_ARC !__has_feature(objc_arc) @@ -16,22 +16,6 @@ // expected-no-diagnostics #endif -typedef signed char BOOL; -@protocol NSObject -- (BOOL)isEqual:(id)object; -- (Class)class; -@end - -@interface NSObject {} -+ (instancetype)alloc; -- (void)dealloc; -- (id)init; -- (id)retain; -- (oneway void)release; -@end - -typedef struct objc_selector *SEL; - // Do not warn about missing release in -dealloc for ivars. @interface MyIvarClass1 : NSObject { @@ -447,6 +431,50 @@ - (void)dealloc; { } @end + +// Don't treat calls to system headers as escapes + +@interface ClassWhereSelfEscapesViaCallToSystem : NSObject +@property (retain) NSObject *ivar1; +@property (retain) NSObject *ivar2; +@property (retain) NSObject *ivar3; +@property (retain) NSObject *ivar4; +@property (retain) NSObject *ivar5; +@property (retain) NSObject *ivar6; +@end + +@implementation ClassWhereSelfEscapesViaCallToSystem +- (void)dealloc; { +#if NON_ARC + [_ivar2 release]; + if (_ivar3) { + [_ivar3 release]; + } +#endif + + [[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget:self]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; + +#if NON_ARC + [_ivar4 release]; + + if (_ivar5) { + [_ivar5 release]; + } +#endif + + [[NSNotificationCenter defaultCenter] removeObserver:self]; + +#if NON_ARC + if (_ivar6) { + [_ivar6 release]; + } + + [super dealloc]; // expected-warning {{The '_ivar1' ivar in 'ClassWhereSelfEscapesViaCallToSystem' was retained by a synthesized property but not released before '[super dealloc]'}} +#endif +} +@end + // Don't warn when value escapes. @interface ClassWhereIvarValueEscapes : NSObject diff --git a/test/Analysis/Inputs/system-header-simulator-for-objc-dealloc.h b/test/Analysis/Inputs/system-header-simulator-for-objc-dealloc.h new file mode 100644 index 00000000000..0b9888a076a --- /dev/null +++ b/test/Analysis/Inputs/system-header-simulator-for-objc-dealloc.h @@ -0,0 +1,29 @@ +#pragma clang system_header + +#define nil ((id)0) + +typedef signed char BOOL; +@protocol NSObject +- (BOOL)isEqual:(id)object; +- (Class)class; +@end + +@interface NSObject {} ++ (instancetype)alloc; +- (void)dealloc; +- (id)init; +- (id)retain; +- (oneway void)release; +@end + +@interface NSRunLoop : NSObject ++ (NSRunLoop *)currentRunLoop; +- (void)cancelPerformSelectorsWithTarget:(id)target; +@end + +@interface NSNotificationCenter : NSObject ++ (NSNotificationCenter *)defaultCenter; +- (void)removeObserver:(id)observer; +@end + +typedef struct objc_selector *SEL; From c4f4e5579333e56915e8a6456c2f6f9c5ceb4156 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Mon, 29 Feb 2016 23:57:10 +0000 Subject: [PATCH 243/742] [analyzer] Teach CheckObjCDealloc about Block_release(). It now treats Block_release(b) as a release in addition to [b release]. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262272 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit d905dbaca5e1b37350678071b8097e4677d313d5) --- .../Checkers/CheckObjCDealloc.cpp | 48 +++++++++++++++---- test/Analysis/DeallocMissingRelease.m | 6 +++ ...system-header-simulator-for-objc-dealloc.h | 3 ++ 3 files changed, 47 insertions(+), 10 deletions(-) diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index f72a1aeb8a9..ea64d9b3d73 100644 --- a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -92,12 +92,13 @@ namespace { class ObjCDeallocChecker : public Checker, check::PreObjCMessage, check::PostObjCMessage, + check::PreCall, check::BeginFunction, check::EndFunction, eval::Assume, check::PointerEscape, check::PreStmt> { - mutable IdentifierInfo *NSObjectII, *SenTestCaseII; + mutable IdentifierInfo *NSObjectII, *SenTestCaseII, *Block_releaseII; mutable Selector DeallocSel, ReleaseSel; std::unique_ptr MissingReleaseBugType; @@ -110,6 +111,7 @@ class ObjCDeallocChecker BugReporter &BR) const; void checkBeginFunction(CheckerContext &Ctx) const; void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond, @@ -152,6 +154,7 @@ class ObjCDeallocChecker const ObjCPropertyDecl * findShadowedPropertyDecl(const ObjCPropertyImplDecl *PropImpl) const; + void transitionToReleaseValue(CheckerContext &C, SymbolRef Value) const; ProgramStateRef removeValueRequiringRelease(ProgramStateRef State, SymbolRef InstanceSym, SymbolRef ValueSym) const; @@ -341,19 +344,26 @@ void ObjCDeallocChecker::checkPreObjCMessage( if (!ReleasedValue) return; - SymbolRef InstanceSym = getInstanceSymbolFromIvarSymbol(ReleasedValue); - if (!InstanceSym) + transitionToReleaseValue(C, ReleasedValue); +} + +/// If we are in -dealloc or -dealloc is on the stack, handle the call if it is +/// call to Block_release(). +void ObjCDeallocChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + const IdentifierInfo *II = Call.getCalleeIdentifier(); + if (II != Block_releaseII) return; - ProgramStateRef InitialState = C.getState(); - ProgramStateRef ReleasedState = - removeValueRequiringRelease(InitialState, InstanceSym, ReleasedValue); + if (Call.getNumArgs() != 1) + return; - if (ReleasedState != InitialState) { - C.addTransition(ReleasedState); - } -} + SymbolRef ReleasedValue = Call.getArgSVal(0).getAsSymbol(); + if (!ReleasedValue) + return; + transitionToReleaseValue(C, ReleasedValue); +} /// If the message was a call to '[super dealloc]', diagnose any missing /// releases. void ObjCDeallocChecker::checkPostObjCMessage( @@ -684,6 +694,7 @@ void ObjCDeallocChecker::initIdentifierInfoAndSelectors( NSObjectII = &Ctx.Idents.get("NSObject"); SenTestCaseII = &Ctx.Idents.get("SenTestCase"); + Block_releaseII = &Ctx.Idents.get("_Block_release"); IdentifierInfo *DeallocII = &Ctx.Idents.get("dealloc"); IdentifierInfo *ReleaseII = &Ctx.Idents.get("release"); @@ -739,6 +750,23 @@ const ObjCPropertyDecl *ObjCDeallocChecker::findShadowedPropertyDecl( return nullptr; } +/// Add a transition noting the release of the given value. +void ObjCDeallocChecker::transitionToReleaseValue(CheckerContext &C, + SymbolRef Value) const { + assert(Value); + SymbolRef InstanceSym = getInstanceSymbolFromIvarSymbol(Value); + if (!InstanceSym) + return; + ProgramStateRef InitialState = C.getState(); + + ProgramStateRef ReleasedState = + removeValueRequiringRelease(InitialState, InstanceSym, Value); + + if (ReleasedState != InitialState) { + C.addTransition(ReleasedState); + } +} + /// Remove the Value requiring a release from the tracked set for /// Instance and return the resultant state. ProgramStateRef ObjCDeallocChecker::removeValueRequiringRelease( diff --git a/test/Analysis/DeallocMissingRelease.m b/test/Analysis/DeallocMissingRelease.m index bb67e436b2b..ff7c4e5971c 100644 --- a/test/Analysis/DeallocMissingRelease.m +++ b/test/Analysis/DeallocMissingRelease.m @@ -125,6 +125,9 @@ @interface MyPropertyClass4 : NSObject { void (^_blockPropertyIvar)(void); } @property (copy) void (^blockProperty)(void); +@property (copy) void (^blockProperty2)(void); +@property (copy) void (^blockProperty3)(void); + @end @implementation MyPropertyClass4 @@ -132,6 +135,9 @@ @implementation MyPropertyClass4 - (void)dealloc { #if NON_ARC + [_blockProperty2 release]; + Block_release(_blockProperty3); + [super dealloc]; // expected-warning {{The '_blockPropertyIvar' ivar in 'MyPropertyClass4' was copied by a synthesized property but not released before '[super dealloc]'}} #endif } diff --git a/test/Analysis/Inputs/system-header-simulator-for-objc-dealloc.h b/test/Analysis/Inputs/system-header-simulator-for-objc-dealloc.h index 0b9888a076a..9850aec6ee8 100644 --- a/test/Analysis/Inputs/system-header-simulator-for-objc-dealloc.h +++ b/test/Analysis/Inputs/system-header-simulator-for-objc-dealloc.h @@ -27,3 +27,6 @@ typedef signed char BOOL; @end typedef struct objc_selector *SEL; + +void _Block_release(const void *aBlock); +#define Block_release(...) _Block_release((const void *)(__VA_ARGS__)) From cf3349c82748b4c4703805c8bd00246c01254d63 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Tue, 1 Mar 2016 00:39:04 +0000 Subject: [PATCH 244/742] [analyzer] Update CheckObjCDealloc diagnostic for missing -dealloc. Update the diagnostic for classes missing -dealloc to mention an instance variable that needs to be released. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262277 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 19a1c8c8d40837d4045c052aeb9b7d06ad9a4d57) --- .../Checkers/CheckObjCDealloc.cpp | 28 +++++++++----- test/Analysis/MissingDealloc.m | 38 ++++++++++++++++--- 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index ea64d9b3d73..c09924cfc2e 100644 --- a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -190,23 +190,27 @@ void ObjCDeallocChecker::checkASTDecl(const ObjCImplementationDecl *D, initIdentifierInfoAndSelectors(Mgr.getASTContext()); const ObjCInterfaceDecl *ID = D->getClassInterface(); + // If the class is known to have a lifecycle with a separate teardown method + // then it may not require a -dealloc method. + if (classHasSeparateTeardown(ID)) + return; // Does the class contain any synthesized properties that are retainable? // If not, skip the check entirely. - bool containsRetainedSynthesizedProperty = false; + const ObjCPropertyImplDecl *PropImplRequiringRelease = nullptr; + bool HasOthers = false; for (const auto *I : D->property_impls()) { if (getDeallocReleaseRequirement(I) == ReleaseRequirement::MustRelease) { - containsRetainedSynthesizedProperty = true; - break; + if (!PropImplRequiringRelease) + PropImplRequiringRelease = I; + else { + HasOthers = true; + break; + } } } - if (!containsRetainedSynthesizedProperty) - return; - - // If the class is known to have a lifecycle with a separate teardown method - // then it may not require a -dealloc method. - if (classHasSeparateTeardown(ID)) + if (!PropImplRequiringRelease) return; const ObjCMethodDecl *MD = nullptr; @@ -224,8 +228,12 @@ void ObjCDeallocChecker::checkASTDecl(const ObjCImplementationDecl *D, std::string Buf; llvm::raw_string_ostream OS(Buf); - OS << "Objective-C class '" << *D << "' lacks a 'dealloc' instance method"; + OS << "'" << *D << "' lacks a 'dealloc' instance method but " + << "must release '" << *PropImplRequiringRelease->getPropertyIvarDecl() + << "'"; + if (HasOthers) + OS << " and others"; PathDiagnosticLocation DLoc = PathDiagnosticLocation::createBegin(D, BR.getSourceManager()); diff --git a/test/Analysis/MissingDealloc.m b/test/Analysis/MissingDealloc.m index ae880546ff9..2dbec92dce4 100644 --- a/test/Analysis/MissingDealloc.m +++ b/test/Analysis/MissingDealloc.m @@ -1,5 +1,12 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.osx.cocoa.Dealloc -fblocks %s 2>&1 | FileCheck -check-prefix=CHECK %s -// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.osx.cocoa.Dealloc -fblocks -triple x86_64-apple-darwin10 -fobjc-arc %s 2>&1 | FileCheck -check-prefix=CHECK-ARC -allow-empty '--implicit-check-not=error:' '--implicit-check-not=warning:' %s +// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.osx.cocoa.Dealloc -fblocks -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.osx.cocoa.Dealloc -fblocks -verify -triple x86_64-apple-darwin10 -fobjc-arc %s + +#define NON_ARC !__has_feature(objc_arc) + +// No diagnostics expected under ARC. +#if !NON_ARC + // expected-no-diagnostics +#endif typedef signed char BOOL; @protocol NSObject @@ -51,7 +58,9 @@ @interface MissingDeallocWithCopyProperty : NSObject @property (copy) NSObject *ivar; @end -// CHECK: MissingDealloc.m:[[@LINE+1]]:1: warning: Objective-C class 'MissingDeallocWithCopyProperty' lacks a 'dealloc' instance method +#if NON_ARC +// expected-warning@+2{{'MissingDeallocWithCopyProperty' lacks a 'dealloc' instance method but must release '_ivar'}} +#endif @implementation MissingDeallocWithCopyProperty @end @@ -59,17 +68,32 @@ @interface MissingDeallocWithRetainProperty : NSObject @property (retain) NSObject *ivar; @end -// CHECK: MissingDealloc.m:[[@LINE+1]]:1: warning: Objective-C class 'MissingDeallocWithRetainProperty' lacks a 'dealloc' instance method +#if NON_ARC +// expected-warning@+2{{'MissingDeallocWithRetainProperty' lacks a 'dealloc' instance method but must release '_ivar'}} +#endif @implementation MissingDeallocWithRetainProperty @end +@interface MissingDeallocWithMultipleProperties : NSObject +@property (retain) NSObject *ivar1; +@property (retain) NSObject *ivar2; +@end + +#if NON_ARC +// expected-warning@+2{{'MissingDeallocWithMultipleProperties' lacks a 'dealloc' instance method but must release '_ivar1' and others}} +#endif +@implementation MissingDeallocWithMultipleProperties +@end + @interface MissingDeallocWithIVarAndRetainProperty : NSObject { NSObject *_ivar2; } @property (retain) NSObject *ivar1; @end -// CHECK: MissingDealloc.m:[[@LINE+1]]:1: warning: Objective-C class 'MissingDeallocWithIVarAndRetainProperty' lacks a 'dealloc' instance method +#if NON_ARC +// expected-warning@+2{{'MissingDeallocWithIVarAndRetainProperty' lacks a 'dealloc' instance method but must release '_ivar1'}} +#endif @implementation MissingDeallocWithIVarAndRetainProperty @end @@ -77,7 +101,9 @@ @interface MissingDeallocWithReadOnlyRetainedProperty : NSObject @property (readonly,retain) NSObject *ivar; @end -// CHECK: MissingDealloc.m:[[@LINE+1]]:1: warning: Objective-C class 'MissingDeallocWithReadOnlyRetainedProperty' lacks a 'dealloc' instance method +#if NON_ARC +// expected-warning@+2{{'MissingDeallocWithReadOnlyRetainedProperty' lacks a 'dealloc' instance method but must release '_ivar'}} +#endif @implementation MissingDeallocWithReadOnlyRetainedProperty @end From d566cc42fa89d8d213b3fbb70edcc20c79ab41fc Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 1 Mar 2016 09:05:55 -0800 Subject: [PATCH 245/742] [Swift] Extend swift_name attribute to describe members. Extend the syntax of the swift_name string to allow "Foo.bar", which specifies that the entity to which the attribute appertains should be imported as a member named "foo" of the type with the Swift name "Foo". For functions that will become instance methods, the special argument name "self" indicates which parameter of the C function will become the "self" parameter. For example, given: typedef struct __attribute__(((swift_name("Foo"))) AAFoo { /* ... */ } AAFoo; void AAFooDoSomething(AAFoo foo, double x, double y) __attribute__((swift_name("Foo.doSomething(self:x:y:)"))); The C type AAFoo will be imported as the Swift type Foo, and AAFooDoSomething will be imported as an instance method "doSomething(x:y:)" of Foo, where the first parameter is "self". Part of implementing SE-0033. --- lib/Sema/SemaDeclAttr.cpp | 38 +++++++++++++++++++++++++++++++++++++- test/SemaObjC/attr-swift.m | 6 ++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index d58fdc7c13f..ffa74072228 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -4449,6 +4449,21 @@ static bool validateSwiftFunctionName(StringRef Name, StringRef BaseName, Parameters; std::tie(BaseName, Parameters) = Name.split('('); + + // Split at the first '.', if it exists, which separates the context + // name from the base name. + StringRef ContextName; + bool IsMember = false; + std::tie(ContextName, BaseName) = BaseName.split('.'); + if (BaseName.empty()) { + BaseName = ContextName; + ContextName = StringRef(); + } else if (ContextName.empty() || !isValidIdentifier(ContextName)) { + return false; + } else { + IsMember = true; + } + if (!isValidIdentifier(BaseName) || BaseName == "_") return false; @@ -4461,12 +4476,23 @@ static bool validateSwiftFunctionName(StringRef Name, if (Parameters.back() != ':') return false; + Optional SelfLocation; StringRef NextParam; do { std::tie(NextParam, Parameters) = Parameters.split(':'); if (!isValidIdentifier(NextParam)) return false; + + // "self" indicates the "self" argument for a member. + if (IsMember && NextParam == "self") { + // More than one "self"? + if (SelfLocation) return false; + + // The "self" location is the current parameter. + SelfLocation = ParamCount; + } + ++ParamCount; } while (!Parameters.empty()); @@ -4533,7 +4559,17 @@ static void handleSwiftName(Sema &S, Decl *D, const AttributeList &Attr) { isa(D) || isa(D) || isa(D) || isa(D) || isa(D) || isa(D) || isa(D)) { - if (!isValidIdentifier(Name)) { + StringRef ContextName, BaseName; + std::tie(ContextName, BaseName) = Name.split('.'); + if (BaseName.empty()) { + BaseName = ContextName; + ContextName = StringRef(); + } else if (!isValidIdentifier(ContextName)) { + S.Diag(ArgLoc, diag::err_attr_swift_name_identifier) << Attr.getName(); + return; + } + + if (!isValidIdentifier(BaseName)) { S.Diag(ArgLoc, diag::err_attr_swift_name_identifier) << Attr.getName(); return; } diff --git a/test/SemaObjC/attr-swift.m b/test/SemaObjC/attr-swift.m index 98c705423f1..80a379f0547 100644 --- a/test/SemaObjC/attr-swift.m +++ b/test/SemaObjC/attr-swift.m @@ -97,6 +97,12 @@ struct __attribute__((swift_name("FooStruct"))) BarStruct { typedef int some_int_type __attribute__((swift_name("SomeInt"))); +struct Point3D createPoint3D(float x, float y, float z) __attribute__((swift_name("Point3D.init(x:y:z:)"))); +struct Point3D rotatePoint3D(Point3D point, float radians) __attribute__((swift_name("Point3D.rotate(self:radius:)"))); +struct Point3D badRotatePoint3D(Point3D point, float radians) __attribute__((swift_name("Point3D.rotate(radius:)"))); // expected-warning {{too few parameters in 'swift_name' attribute (expected 2; got 1)}} + +extern struct Point3D identityPoint __attribute__((swift_name("Point3D.identity"))); + // --- swift_error --- @class NSError; From 7c358148ac83ba376ce0145e0e1d73b87b0a05ab Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 1 Mar 2016 12:55:29 -0800 Subject: [PATCH 246/742] [Swift] Extend swift_name to support getters and setters. Extend the string format of swift_name to support labeling a function as a getter or setter for a given property name, so that the getter (and optional setter) function(s) will be imported into Swift as a property. Slightly extended from what SE-0033 actually requires, but useful in its own right. --- lib/Sema/SemaDeclAttr.cpp | 31 ++++++++++++++++++++++++++++++- test/SemaObjC/attr-swift.m | 24 ++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index ffa74072228..b8bfc614f59 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -4444,6 +4444,19 @@ static bool validateSwiftFunctionName(StringRef Name, unsigned &ParamCount, bool &IsSingleParamInit) { ParamCount = 0; + + // Check whether this will be mapped to a getter or setter of a + // property. + bool isGetter = false; + bool isSetter = false; + if (Name.startswith("getter:")) { + isGetter = true; + Name = Name.substr(7); + } else if (Name.startswith("setter:")) { + isSetter = true; + Name = Name.substr(7); + } + if (Name.back() != ')') return false; @@ -4470,8 +4483,13 @@ static bool validateSwiftFunctionName(StringRef Name, if (Parameters.empty()) return false; Parameters = Parameters.drop_back(); // ')' - if (Parameters.empty()) + + if (Parameters.empty()) { + // Setters must have at least one parameter. + if (isSetter) return false; + return true; + } if (Parameters.back() != ':') return false; @@ -4499,6 +4517,17 @@ static bool validateSwiftFunctionName(StringRef Name, IsSingleParamInit = (ParamCount == 1 && BaseName == "init" && NextParam != "_"); + // Check the number of parameters for a getter/setter. + if (isGetter || isSetter) { + // Setters have one parameter for the new value. + unsigned NumExpectedParams = isSetter ? 1 : 0; + + // Instance methods have one parameter for "self". + if (SelfLocation) ++NumExpectedParams; + + if (ParamCount != NumExpectedParams) return true; + } + return true; } diff --git a/test/SemaObjC/attr-swift.m b/test/SemaObjC/attr-swift.m index 80a379f0547..28042635f75 100644 --- a/test/SemaObjC/attr-swift.m +++ b/test/SemaObjC/attr-swift.m @@ -103,6 +103,30 @@ struct __attribute__((swift_name("FooStruct"))) BarStruct { extern struct Point3D identityPoint __attribute__((swift_name("Point3D.identity"))); +// Getters and setters. +float Point3DGetMagnitude(Point3D point) __attribute__((swift_name("getter:Point3D.magnitude(self:)"))); + +float Point3DGetRadius(Point3D point) __attribute__((swift_name("getter:Point3D.radius(self:)"))); +void Point3DSetRadius(Point3D point, float radius) __attribute__((swift_name("setter:Point3D.radius(self:_:)"))); + +Point3D getCurrentPoint3D(void) __attribute__((swift_name("getter:currentPoint3D()"))); + +void setCurrentPoint3D(Point3D point) __attribute__((swift_name("setter:currentPoint3D(_:)"))); + +Point3D getLastPoint3D(void) __attribute__((swift_name("getter:lastPoint3D()"))); + +void setLastPoint3D(Point3D point) __attribute__((swift_name("setter:lastPoint3D(_:)"))); + +Point3D getZeroPoint() __attribute__((swift_name("getter:Point3D()"))); +void setZeroPoint(Point3D point) __attribute__((swift_name("setter:Point3D(_:)"))); + +Point3D badGetter1(int x) __attribute__((swift_name("getter:bad1(_:))"))); // expected-error{{parameter of 'swift_name' attribute must be a Swift function name string}} +void badSetter1() __attribute__((swift_name("getter:bad1())"))); // expected-error{{parameter of 'swift_name' attribute must be a Swift function name string}} + +Point3D badGetter2(Point3D point) __attribute__((swift_name("getter:bad2(_:))"))); // expected-error{{parameter of 'swift_name' attribute must be a Swift function name string}} + +void badSetter2(Point3D point) __attribute__((swift_name("setter:bad2(self:))"))); // expected-error{{parameter of 'swift_name' attribute must be a Swift function name string}} + // --- swift_error --- @class NSError; From df08592096a024c2dd51139f48ee22b1c4a92612 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 1 Mar 2016 09:05:55 -0800 Subject: [PATCH 247/742] [Swift] Extend swift_name attribute to describe members. Extend the syntax of the swift_name string to allow "Foo.bar", which specifies that the entity to which the attribute appertains should be imported as a member named "foo" of the type with the Swift name "Foo". For functions that will become instance methods, the special argument name "self" indicates which parameter of the C function will become the "self" parameter. For example, given: typedef struct __attribute__(((swift_name("Foo"))) AAFoo { /* ... */ } AAFoo; void AAFooDoSomething(AAFoo foo, double x, double y) __attribute__((swift_name("Foo.doSomething(self:x:y:)"))); The C type AAFoo will be imported as the Swift type Foo, and AAFooDoSomething will be imported as an instance method "doSomething(x:y:)" of Foo, where the first parameter is "self". Part of implementing SE-0033. --- lib/Sema/SemaDeclAttr.cpp | 38 +++++++++++++++++++++++++++++++++++++- test/SemaObjC/attr-swift.m | 6 ++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index c45becf0ab4..9c45d221bf1 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -4454,6 +4454,21 @@ static bool validateSwiftFunctionName(StringRef Name, StringRef BaseName, Parameters; std::tie(BaseName, Parameters) = Name.split('('); + + // Split at the first '.', if it exists, which separates the context + // name from the base name. + StringRef ContextName; + bool IsMember = false; + std::tie(ContextName, BaseName) = BaseName.split('.'); + if (BaseName.empty()) { + BaseName = ContextName; + ContextName = StringRef(); + } else if (ContextName.empty() || !isValidIdentifier(ContextName)) { + return false; + } else { + IsMember = true; + } + if (!isValidIdentifier(BaseName) || BaseName == "_") return false; @@ -4466,12 +4481,23 @@ static bool validateSwiftFunctionName(StringRef Name, if (Parameters.back() != ':') return false; + Optional SelfLocation; StringRef NextParam; do { std::tie(NextParam, Parameters) = Parameters.split(':'); if (!isValidIdentifier(NextParam)) return false; + + // "self" indicates the "self" argument for a member. + if (IsMember && NextParam == "self") { + // More than one "self"? + if (SelfLocation) return false; + + // The "self" location is the current parameter. + SelfLocation = ParamCount; + } + ++ParamCount; } while (!Parameters.empty()); @@ -4538,7 +4564,17 @@ static void handleSwiftName(Sema &S, Decl *D, const AttributeList &Attr) { isa(D) || isa(D) || isa(D) || isa(D) || isa(D) || isa(D) || isa(D)) { - if (!isValidIdentifier(Name)) { + StringRef ContextName, BaseName; + std::tie(ContextName, BaseName) = Name.split('.'); + if (BaseName.empty()) { + BaseName = ContextName; + ContextName = StringRef(); + } else if (!isValidIdentifier(ContextName)) { + S.Diag(ArgLoc, diag::err_attr_swift_name_identifier) << Attr.getName(); + return; + } + + if (!isValidIdentifier(BaseName)) { S.Diag(ArgLoc, diag::err_attr_swift_name_identifier) << Attr.getName(); return; } diff --git a/test/SemaObjC/attr-swift.m b/test/SemaObjC/attr-swift.m index 98c705423f1..80a379f0547 100644 --- a/test/SemaObjC/attr-swift.m +++ b/test/SemaObjC/attr-swift.m @@ -97,6 +97,12 @@ struct __attribute__((swift_name("FooStruct"))) BarStruct { typedef int some_int_type __attribute__((swift_name("SomeInt"))); +struct Point3D createPoint3D(float x, float y, float z) __attribute__((swift_name("Point3D.init(x:y:z:)"))); +struct Point3D rotatePoint3D(Point3D point, float radians) __attribute__((swift_name("Point3D.rotate(self:radius:)"))); +struct Point3D badRotatePoint3D(Point3D point, float radians) __attribute__((swift_name("Point3D.rotate(radius:)"))); // expected-warning {{too few parameters in 'swift_name' attribute (expected 2; got 1)}} + +extern struct Point3D identityPoint __attribute__((swift_name("Point3D.identity"))); + // --- swift_error --- @class NSError; From 335669e88b32374ca0829750b2ee4f7251044066 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 1 Mar 2016 12:55:29 -0800 Subject: [PATCH 248/742] [Swift] Extend swift_name to support getters and setters. Extend the string format of swift_name to support labeling a function as a getter or setter for a given property name, so that the getter (and optional setter) function(s) will be imported into Swift as a property. Slightly extended from what SE-0033 actually requires, but useful in its own right. --- lib/Sema/SemaDeclAttr.cpp | 31 ++++++++++++++++++++++++++++++- test/SemaObjC/attr-swift.m | 24 ++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 9c45d221bf1..79da40f2341 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -4449,6 +4449,19 @@ static bool validateSwiftFunctionName(StringRef Name, unsigned &ParamCount, bool &IsSingleParamInit) { ParamCount = 0; + + // Check whether this will be mapped to a getter or setter of a + // property. + bool isGetter = false; + bool isSetter = false; + if (Name.startswith("getter:")) { + isGetter = true; + Name = Name.substr(7); + } else if (Name.startswith("setter:")) { + isSetter = true; + Name = Name.substr(7); + } + if (Name.back() != ')') return false; @@ -4475,8 +4488,13 @@ static bool validateSwiftFunctionName(StringRef Name, if (Parameters.empty()) return false; Parameters = Parameters.drop_back(); // ')' - if (Parameters.empty()) + + if (Parameters.empty()) { + // Setters must have at least one parameter. + if (isSetter) return false; + return true; + } if (Parameters.back() != ':') return false; @@ -4504,6 +4522,17 @@ static bool validateSwiftFunctionName(StringRef Name, IsSingleParamInit = (ParamCount == 1 && BaseName == "init" && NextParam != "_"); + // Check the number of parameters for a getter/setter. + if (isGetter || isSetter) { + // Setters have one parameter for the new value. + unsigned NumExpectedParams = isSetter ? 1 : 0; + + // Instance methods have one parameter for "self". + if (SelfLocation) ++NumExpectedParams; + + if (ParamCount != NumExpectedParams) return true; + } + return true; } diff --git a/test/SemaObjC/attr-swift.m b/test/SemaObjC/attr-swift.m index 80a379f0547..28042635f75 100644 --- a/test/SemaObjC/attr-swift.m +++ b/test/SemaObjC/attr-swift.m @@ -103,6 +103,30 @@ struct __attribute__((swift_name("FooStruct"))) BarStruct { extern struct Point3D identityPoint __attribute__((swift_name("Point3D.identity"))); +// Getters and setters. +float Point3DGetMagnitude(Point3D point) __attribute__((swift_name("getter:Point3D.magnitude(self:)"))); + +float Point3DGetRadius(Point3D point) __attribute__((swift_name("getter:Point3D.radius(self:)"))); +void Point3DSetRadius(Point3D point, float radius) __attribute__((swift_name("setter:Point3D.radius(self:_:)"))); + +Point3D getCurrentPoint3D(void) __attribute__((swift_name("getter:currentPoint3D()"))); + +void setCurrentPoint3D(Point3D point) __attribute__((swift_name("setter:currentPoint3D(_:)"))); + +Point3D getLastPoint3D(void) __attribute__((swift_name("getter:lastPoint3D()"))); + +void setLastPoint3D(Point3D point) __attribute__((swift_name("setter:lastPoint3D(_:)"))); + +Point3D getZeroPoint() __attribute__((swift_name("getter:Point3D()"))); +void setZeroPoint(Point3D point) __attribute__((swift_name("setter:Point3D(_:)"))); + +Point3D badGetter1(int x) __attribute__((swift_name("getter:bad1(_:))"))); // expected-error{{parameter of 'swift_name' attribute must be a Swift function name string}} +void badSetter1() __attribute__((swift_name("getter:bad1())"))); // expected-error{{parameter of 'swift_name' attribute must be a Swift function name string}} + +Point3D badGetter2(Point3D point) __attribute__((swift_name("getter:bad2(_:))"))); // expected-error{{parameter of 'swift_name' attribute must be a Swift function name string}} + +void badSetter2(Point3D point) __attribute__((swift_name("setter:bad2(self:))"))); // expected-error{{parameter of 'swift_name' attribute must be a Swift function name string}} + // --- swift_error --- @class NSError; From 1b827cd4a78eac7cb7ad53e8eb10d53a6c8fc538 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Tue, 1 Mar 2016 14:41:01 -0800 Subject: [PATCH 249/742] Temporarily r257403 which depends on r256907 to workaround a crash. This reverts commit a454e263124ae7949ab96d33987dc590c3ccf90c. --- lib/Sema/SemaDecl.cpp | 8 ++++---- test/Modules/tag-injection.c | 18 ------------------ test/Sema/decl-in-prototype.c | 3 --- 3 files changed, 4 insertions(+), 25 deletions(-) delete mode 100644 test/Modules/tag-injection.c diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 62ed26006ec..26087a1bac1 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -12327,8 +12327,7 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, } else if (TUK == TUK_Reference && (PrevTagDecl->getFriendObjectKind() == Decl::FOK_Undeclared || - PP.getModuleContainingLocation( - PrevDecl->getLocation()) != + getOwningModule(PrevDecl) != PP.getModuleContainingLocation(KWLoc)) && SS.isEmpty()) { // This declaration is a reference to an existing entity, but @@ -12338,7 +12337,8 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, // the declaration would have meant the same thing if no prior // declaration were found, that is, if it was found in the same // scope where we would have injected a declaration. - if (!getTagInjectionContext(CurContext)->getRedeclContext() + if (!getTagInjectionContext(CurContext) + ->getRedeclContext() ->Equals(PrevDecl->getDeclContext()->getRedeclContext())) return PrevTagDecl; // This is in the injected scope, create a new declaration in @@ -12645,7 +12645,7 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, << Name; Invalid = true; } - } else if (!PrevDecl) { + } else { Diag(Loc, diag::warn_decl_in_param_list) << Context.getTagDeclType(New); } DeclsInPrototypeScope.push_back(New); diff --git a/test/Modules/tag-injection.c b/test/Modules/tag-injection.c deleted file mode 100644 index 5bb15477e2e..00000000000 --- a/test/Modules/tag-injection.c +++ /dev/null @@ -1,18 +0,0 @@ -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: echo 'struct a;' > %t/a.h -// RUN: echo 'struct b {}; void foo(struct b*);' > %t/b.h -// RUN: echo 'module X { module a { header "a.h" } module b { header "b.h" } }' > %t/x.modulemap -// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -fmodule-map-file=%t/x.modulemap %s -I%t -verify - -#include "a.h" - -void f(struct a *p); - -// FIXME: We should warn that 'b' will not be visible outside of this function, -// but we merge this 'b' with X.b's 'b' because we don't yet implement C's -// "compatible types" rule. -void g(struct b *p); - -struct b b; // expected-error {{definition of 'b' must be imported from module 'X.b' before it is required}} -// expected-note@b.h:1 {{here}} diff --git a/test/Sema/decl-in-prototype.c b/test/Sema/decl-in-prototype.c index 3b8a3b86037..4f581aa54e5 100644 --- a/test/Sema/decl-in-prototype.c +++ b/test/Sema/decl-in-prototype.c @@ -35,6 +35,3 @@ void f6(struct z {int b;} c) { // expected-warning {{declaration of 'struct z' w void pr19018_1 (enum e19018 { qq } x); // expected-warning{{declaration of 'enum e19018' will not be visible outside of this function}} enum e19018 qq; //expected-error{{tentative definition has type 'enum e19018' that is never completed}} \ //expected-note{{forward declaration of 'enum e19018'}} - -// Only warn once, even if we create two declarations. -void f(struct q *, struct __attribute__((aligned(4))) q *); // expected-warning {{will not be visible outside}} From 5e2f497b8ca8f3306f81ffbe9c319aa1c457e74e Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Tue, 1 Mar 2016 14:42:35 -0800 Subject: [PATCH 250/742] Temporarily r257251 which depends on r256907 to workaround a crash. This reverts commit b3db27cd07f9e62076df5f571b359f9e90f779ec. --- lib/Sema/SemaDecl.cpp | 45 ++++++++++++---------------------- test/Modules/tag-injection.cpp | 7 ++---- 2 files changed, 17 insertions(+), 35 deletions(-) diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 26087a1bac1..67aa6ae3f12 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -11826,28 +11826,6 @@ static bool isAcceptableTagRedeclContext(Sema &S, DeclContext *OldDC, return false; } -/// Find the DeclContext in which a tag is implicitly declared if we see an -/// elaborated type specifier in the specified context, and lookup finds -/// nothing. -static DeclContext *getTagInjectionContext(DeclContext *DC) { - while (!DC->isFileContext() && !DC->isFunctionOrMethod()) - DC = DC->getParent(); - return DC; -} - -/// Find the Scope in which a tag is implicitly declared if we see an -/// elaborated type specifier in the specified context, and lookup finds -/// nothing. -static Scope *getTagInjectionScope(Scope *S, const LangOptions &LangOpts) { - while (S->isClassScope() || - (LangOpts.CPlusPlus && - S->isFunctionPrototypeScope()) || - ((S->getFlags() & Scope::DeclScope) == 0) || - (S->getEntity() && S->getEntity()->isTransparentContext())) - S = S->getParent(); - return S; -} - /// \brief This is invoked when we see 'struct foo' or 'struct {'. In the /// former case, Name will be non-null. In the later case, Name will be null. /// TagSpec indicates what kind of tag this is. TUK indicates whether this is a @@ -12164,10 +12142,16 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, // Find the context where we'll be declaring the tag. // FIXME: We would like to maintain the current DeclContext as the // lexical context, - SearchDC = getTagInjectionContext(SearchDC); + while (!SearchDC->isFileContext() && !SearchDC->isFunctionOrMethod()) + SearchDC = SearchDC->getParent(); // Find the scope where we'll be declaring the tag. - S = getTagInjectionScope(S, getLangOpts()); + while (S->isClassScope() || + (getLangOpts().CPlusPlus && + S->isFunctionPrototypeScope()) || + ((S->getFlags() & Scope::DeclScope) == 0) || + (S->getEntity() && S->getEntity()->isTransparentContext())) + S = S->getParent(); } else { assert(TUK == TUK_Friend); // C++ [namespace.memdef]p3: @@ -12337,13 +12321,14 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, // the declaration would have meant the same thing if no prior // declaration were found, that is, if it was found in the same // scope where we would have injected a declaration. - if (!getTagInjectionContext(CurContext) - ->getRedeclContext() - ->Equals(PrevDecl->getDeclContext()->getRedeclContext())) + DeclContext *InjectedDC = CurContext; + while (!InjectedDC->isFileContext() && + !InjectedDC->isFunctionOrMethod()) + InjectedDC = InjectedDC->getParent(); + if (!InjectedDC->getRedeclContext()->Equals( + PrevDecl->getDeclContext()->getRedeclContext())) return PrevTagDecl; - // This is in the injected scope, create a new declaration in - // that scope. - S = getTagInjectionScope(S, getLangOpts()); + // This is in the injected scope, create a new declaration. } else { return PrevTagDecl; } diff --git a/test/Modules/tag-injection.cpp b/test/Modules/tag-injection.cpp index e55598b0620..75c8b5fecdb 100644 --- a/test/Modules/tag-injection.cpp +++ b/test/Modules/tag-injection.cpp @@ -1,15 +1,12 @@ // RUN: rm -rf %t // RUN: mkdir %t -// RUN: echo 'struct tm;' > %t/a.h -// RUN: echo 'struct X {}; void foo(struct tm*);' > %t/b.h +// RUN: touch %t/a.h +// RUN: echo 'struct X {};' > %t/b.h // RUN: echo 'module X { module a { header "a.h" } module b { header "b.h" } }' > %t/x.modulemap -// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -x c++ -fmodule-map-file=%t/x.modulemap %s -I%t -verify -std=c++11 // RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -x c++ -fmodule-map-file=%t/x.modulemap %s -I%t -verify -fmodules-local-submodule-visibility -std=c++11 #include "a.h" -using ::tm; - struct A { // This use of 'struct X' makes the declaration (but not definition) of X visible. virtual void f(struct X *p); From 42ed42b5303ca5d7a576a928b356202e819cb80b Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Tue, 1 Mar 2016 14:43:10 -0800 Subject: [PATCH 251/742] Temporarily r256907 to workaround a crash when mixing modules and PCH. This reverts commit a81b22be300e72675708aacc9e7fe53ba01c67f3. --- lib/Sema/SemaDecl.cpp | 39 +++++++++------------------------- test/Modules/tag-injection.cpp | 22 ------------------- 2 files changed, 10 insertions(+), 51 deletions(-) delete mode 100644 test/Modules/tag-injection.cpp diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 67aa6ae3f12..6dd210fb497 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -12304,35 +12304,16 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, if (!Invalid) { // If this is a use, just return the declaration we found, unless // we have attributes. - if (TUK == TUK_Reference || TUK == TUK_Friend) { - if (Attr) { - // FIXME: Diagnose these attributes. For now, we create a new - // declaration to hold them. - } else if (TUK == TUK_Reference && - (PrevTagDecl->getFriendObjectKind() == - Decl::FOK_Undeclared || - getOwningModule(PrevDecl) != - PP.getModuleContainingLocation(KWLoc)) && - SS.isEmpty()) { - // This declaration is a reference to an existing entity, but - // has different visibility from that entity: it either makes - // a friend visible or it makes a type visible in a new module. - // In either case, create a new declaration. We only do this if - // the declaration would have meant the same thing if no prior - // declaration were found, that is, if it was found in the same - // scope where we would have injected a declaration. - DeclContext *InjectedDC = CurContext; - while (!InjectedDC->isFileContext() && - !InjectedDC->isFunctionOrMethod()) - InjectedDC = InjectedDC->getParent(); - if (!InjectedDC->getRedeclContext()->Equals( - PrevDecl->getDeclContext()->getRedeclContext())) - return PrevTagDecl; - // This is in the injected scope, create a new declaration. - } else { - return PrevTagDecl; - } - } + + // FIXME: In the future, return a variant or some other clue + // for the consumer of this Decl to know it doesn't own it. + // For our current ASTs this shouldn't be a problem, but will + // need to be changed with DeclGroups. + if (!Attr && + ((TUK == TUK_Reference && + (!PrevTagDecl->getFriendObjectKind() || getLangOpts().MicrosoftExt)) + || TUK == TUK_Friend)) + return PrevTagDecl; // Diagnose attempts to redefine a tag. if (TUK == TUK_Definition) { diff --git a/test/Modules/tag-injection.cpp b/test/Modules/tag-injection.cpp deleted file mode 100644 index 75c8b5fecdb..00000000000 --- a/test/Modules/tag-injection.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: touch %t/a.h -// RUN: echo 'struct X {};' > %t/b.h -// RUN: echo 'module X { module a { header "a.h" } module b { header "b.h" } }' > %t/x.modulemap -// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -x c++ -fmodule-map-file=%t/x.modulemap %s -I%t -verify -fmodules-local-submodule-visibility -std=c++11 - -#include "a.h" - -struct A { - // This use of 'struct X' makes the declaration (but not definition) of X visible. - virtual void f(struct X *p); -}; - -namespace N { - struct B : A { - void f(struct X *q) override; - }; -} - -X x; // expected-error {{definition of 'X' must be imported from module 'X.b' before it is required}} -// expected-note@b.h:1 {{here}} From 8a029cf1a11b489271235619e2fd0213d74165a7 Mon Sep 17 00:00:00 2001 From: Chris Bieneman Date: Wed, 2 Mar 2016 00:27:15 +0000 Subject: [PATCH 252/742] [CMake] Add convenience target clang-test-depends to build test dependencies. This is useful when paired with the distribution targets to build prerequisites for running tests. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262429 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8dd64d1614a..f1a5838f6bd 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -71,6 +71,8 @@ if( NOT CLANG_BUILT_STANDALONE ) ) endif() +add_custom_target(clang-test-depends DEPENDS ${CLANG_TEST_DEPS}) + add_lit_testsuite(check-clang "Running the Clang regression tests" ${CMAKE_CURRENT_BINARY_DIR} #LIT ${LLVM_LIT} From f0b2b93c0520bcb0af8a7331c8540c3f447c69b8 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Wed, 2 Mar 2016 21:22:48 +0000 Subject: [PATCH 253/742] [analyzer] Fix capitalization in ObjCSuperDeallocChecker diagnostic. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262520 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit fbec52f0715d6f4459aef26f17e5311fb8f903f1) --- .../Checkers/ObjCSuperDeallocChecker.cpp | 2 +- test/Analysis/DeallocUseAfterFreeErrors.m | 20 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp index 8618eeb0dfe..eb31321895f 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp @@ -165,7 +165,7 @@ void ObjCSuperDeallocChecker::checkLocation(SVal L, bool IsLoad, const Stmt *S, std::string Buf; llvm::raw_string_ostream OS(Buf); if (IvarRegion) { - OS << "use of instance variable '" << *IvarRegion->getDecl() << + OS << "Use of instance variable '" << *IvarRegion->getDecl() << "' after 'self' has been deallocated"; Desc = OS.str(); } diff --git a/test/Analysis/DeallocUseAfterFreeErrors.m b/test/Analysis/DeallocUseAfterFreeErrors.m index cfb202b6507..2902be694bb 100644 --- a/test/Analysis/DeallocUseAfterFreeErrors.m +++ b/test/Analysis/DeallocUseAfterFreeErrors.m @@ -36,8 +36,8 @@ - (instancetype)initWithIvar:(NSObject *)ivar { } - (void)dealloc { [super dealloc]; // expected-note {{[super dealloc] called here}} - [_ivar release]; // expected-warning {{use of instance variable '_ivar' after 'self' has been deallocated}} - // expected-note@-1 {{use of instance variable '_ivar' after 'self' has been deallocated}} + [_ivar release]; // expected-warning {{Use of instance variable '_ivar' after 'self' has been deallocated}} + // expected-note@-1 {{Use of instance variable '_ivar' after 'self' has been deallocated}} } @end @@ -56,8 +56,8 @@ - (instancetype)initWithDelegate:(NSObject *)delegate { } - (void)dealloc { [super dealloc]; // expected-note {{[super dealloc] called here}} - _delegate = nil; // expected-warning {{use of instance variable '_delegate' after 'self' has been deallocated}} - // expected-note@-1 {{use of instance variable '_delegate' after 'self' has been deallocated}} + _delegate = nil; // expected-warning {{Use of instance variable '_delegate' after 'self' has been deallocated}} + // expected-note@-1 {{Use of instance variable '_delegate' after 'self' has been deallocated}} } @end @@ -74,8 +74,8 @@ @interface SuperDeallocThenAssignIvarField : NSObject { @implementation SuperDeallocThenAssignIvarField - (void)dealloc { [super dealloc]; // expected-note {{[super dealloc] called here}} - _s.f = 7; // expected-warning {{use of instance variable '_s' after 'self' has been deallocated}} - // expected-note@-1 {{use of instance variable '_s' after 'self' has been deallocated}} + _s.f = 7; // expected-warning {{Use of instance variable '_s' after 'self' has been deallocated}} + // expected-note@-1 {{Use of instance variable '_s' after 'self' has been deallocated}} } @end @@ -93,8 +93,8 @@ @interface SuperDeallocThenAssignIvarIvar : NSObject { @implementation SuperDeallocThenAssignIvarIvar - (void)dealloc { [super dealloc]; // expected-note {{[super dealloc] called here}} - _ivar->_otherIvar = 7; // expected-warning {{use of instance variable '_ivar' after 'self' has been deallocated}} - // expected-note@-1 {{use of instance variable '_ivar' after 'self' has been deallocated}} + _ivar->_otherIvar = 7; // expected-warning {{Use of instance variable '_ivar' after 'self' has been deallocated}} + // expected-note@-1 {{Use of instance variable '_ivar' after 'self' has been deallocated}} } @end @@ -106,8 +106,8 @@ @interface SuperDeallocThenAssignSelfIvar : NSObject { @implementation SuperDeallocThenAssignSelfIvar - (void)dealloc { [super dealloc]; // expected-note {{[super dealloc] called here}} - self->_ivar = nil; // expected-warning {{use of instance variable '_ivar' after 'self' has been deallocated}} - // expected-note@-1 {{use of instance variable '_ivar' after 'self' has been deallocated}} + self->_ivar = nil; // expected-warning {{Use of instance variable '_ivar' after 'self' has been deallocated}} + // expected-note@-1 {{Use of instance variable '_ivar' after 'self' has been deallocated}} } @end From f2e7a12b4106a2895b6f12425f29e18ceb303236 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Wed, 2 Mar 2016 21:50:54 +0000 Subject: [PATCH 254/742] [analyzer] Move ObjCDeallocChecker out of the alpha package. It will now be on by default on Darwin. rdar://problem/6927496 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262524 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 344e6f538935bb8eaeb05a9e0ff7e81dc723f0c9) --- lib/StaticAnalyzer/Checkers/Checkers.td | 8 ++++---- test/Analysis/DeallocMissingRelease.m | 4 ++-- test/Analysis/MissingDealloc.m | 4 ++-- test/Analysis/PR2978.m | 2 +- test/Analysis/properties.m | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/StaticAnalyzer/Checkers/Checkers.td b/lib/StaticAnalyzer/Checkers/Checkers.td index 7748d9cbc83..16b8279c53c 100644 --- a/lib/StaticAnalyzer/Checkers/Checkers.td +++ b/lib/StaticAnalyzer/Checkers/Checkers.td @@ -505,14 +505,14 @@ def ObjCGenericsChecker : Checker<"ObjCGenerics">, HelpText<"Check for type errors when using Objective-C generics">, DescFile<"DynamicTypePropagation.cpp">; -} // end "osx.cocoa" - -let ParentPackage = CocoaAlpha in { - def ObjCDeallocChecker : Checker<"Dealloc">, HelpText<"Warn about Objective-C classes that lack a correct implementation of -dealloc">, DescFile<"CheckObjCDealloc.cpp">; +} // end "osx.cocoa" + +let ParentPackage = CocoaAlpha in { + def ObjCSuperDeallocChecker : Checker<"SuperDealloc">, HelpText<"Warn about improper use of '[super dealloc]' in Objective-C">, DescFile<"ObjCSuperDeallocChecker.cpp">; diff --git a/test/Analysis/DeallocMissingRelease.m b/test/Analysis/DeallocMissingRelease.m index ff7c4e5971c..383bacb539f 100644 --- a/test/Analysis/DeallocMissingRelease.m +++ b/test/Analysis/DeallocMissingRelease.m @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.osx.cocoa.Dealloc -fblocks -verify %s -// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.osx.cocoa.Dealloc -fblocks -triple x86_64-apple-darwin10 -fobjc-arc -fobjc-runtime-has-weak -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=osx.cocoa.Dealloc -fblocks -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=osx.cocoa.Dealloc -fblocks -triple x86_64-apple-darwin10 -fobjc-arc -fobjc-runtime-has-weak -verify %s #include "Inputs/system-header-simulator-for-objc-dealloc.h" diff --git a/test/Analysis/MissingDealloc.m b/test/Analysis/MissingDealloc.m index 2dbec92dce4..248dc514c87 100644 --- a/test/Analysis/MissingDealloc.m +++ b/test/Analysis/MissingDealloc.m @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.osx.cocoa.Dealloc -fblocks -verify %s -// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.osx.cocoa.Dealloc -fblocks -verify -triple x86_64-apple-darwin10 -fobjc-arc %s +// RUN: %clang_cc1 -analyze -analyzer-checker=osx.cocoa.Dealloc -fblocks -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=osx.cocoa.Dealloc -fblocks -verify -triple x86_64-apple-darwin10 -fobjc-arc %s #define NON_ARC !__has_feature(objc_arc) diff --git a/test/Analysis/PR2978.m b/test/Analysis/PR2978.m index bb0fc414566..b609da5aac2 100644 --- a/test/Analysis/PR2978.m +++ b/test/Analysis/PR2978.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.core -analyzer-checker=alpha.osx.cocoa.Dealloc %s -verify +// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.core,osx.cocoa.Dealloc %s -verify // Tests for the checker which checks missing/extra ivar 'release' calls // in dealloc. diff --git a/test/Analysis/properties.m b/test/Analysis/properties.m index 98d6d7ce1d7..bda2149409c 100644 --- a/test/Analysis/properties.m +++ b/test/Analysis/properties.m @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount,alpha.osx.cocoa.Dealloc,debug.ExprInspection -analyzer-store=region -verify -Wno-objc-root-class %s -// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount,alpha.osx.cocoa.Dealloc,debug.ExprInspection -analyzer-store=region -verify -Wno-objc-root-class -fobjc-arc %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount,osx.cocoa.Dealloc,debug.ExprInspection -analyzer-store=region -verify -Wno-objc-root-class %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.RetainCount,osx.cocoa.Dealloc,debug.ExprInspection -analyzer-store=region -verify -Wno-objc-root-class -fobjc-arc %s void clang_analyzer_eval(int); From 0a541d4665ae991b22f558b3d6bef37af2bde396 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Wed, 2 Mar 2016 22:01:03 +0000 Subject: [PATCH 255/742] [analyzer] Move ObjCSuperDeallocChecker out of the alpha package. It will now be on by default on Darwin. rdar://problem/6953275 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262526 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 9a2603bae5fe7dddb08352b681adfe1d70beb609) --- lib/StaticAnalyzer/Checkers/Checkers.td | 8 ++++---- test/Analysis/DeallocUseAfterFreeErrors.m | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/StaticAnalyzer/Checkers/Checkers.td b/lib/StaticAnalyzer/Checkers/Checkers.td index 16b8279c53c..a674af7be0e 100644 --- a/lib/StaticAnalyzer/Checkers/Checkers.td +++ b/lib/StaticAnalyzer/Checkers/Checkers.td @@ -509,14 +509,14 @@ def ObjCDeallocChecker : Checker<"Dealloc">, HelpText<"Warn about Objective-C classes that lack a correct implementation of -dealloc">, DescFile<"CheckObjCDealloc.cpp">; -} // end "osx.cocoa" - -let ParentPackage = CocoaAlpha in { - def ObjCSuperDeallocChecker : Checker<"SuperDealloc">, HelpText<"Warn about improper use of '[super dealloc]' in Objective-C">, DescFile<"ObjCSuperDeallocChecker.cpp">; +} // end "osx.cocoa" + +let ParentPackage = CocoaAlpha in { + def InstanceVariableInvalidation : Checker<"InstanceVariableInvalidation">, HelpText<"Check that the invalidatable instance variables are invalidated in the methods annotated with objc_instance_variable_invalidator">, DescFile<"IvarInvalidationChecker.cpp">; diff --git a/test/Analysis/DeallocUseAfterFreeErrors.m b/test/Analysis/DeallocUseAfterFreeErrors.m index 2902be694bb..3feeb6d7482 100644 --- a/test/Analysis/DeallocUseAfterFreeErrors.m +++ b/test/Analysis/DeallocUseAfterFreeErrors.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.osx.cocoa.SuperDealloc,debug.ExprInspection -analyzer-output=text -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx.cocoa.SuperDealloc,debug.ExprInspection -analyzer-output=text -verify %s void clang_analyzer_warnIfReached(); From b20dc7cc17a0538279872fee95d5b2d98efaa470 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Thu, 3 Mar 2016 05:33:54 +0000 Subject: [PATCH 256/742] [index] Report references of ObjC super class/protocols in interfaces and protocols. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262584 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Index/IndexDecl.cpp | 37 ++++++++++++++++++++++++++++------ test/Index/Core/index-source.m | 20 ++++++++++++++++++ 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/lib/Index/IndexDecl.cpp b/lib/Index/IndexDecl.cpp index 76f68e564c9..af438f36843 100644 --- a/lib/Index/IndexDecl.cpp +++ b/lib/Index/IndexDecl.cpp @@ -14,6 +14,12 @@ using namespace clang; using namespace index; +#define TRY_TO(CALL_EXPR) \ + do { \ + if (!CALL_EXPR) \ + return false; \ + } while (0) + namespace { class IndexingDeclVisitor : public ConstDeclVisitor { @@ -196,11 +202,30 @@ class IndexingDeclVisitor : public ConstDeclVisitor { return true; } + bool handleReferencedProtocols(const ObjCProtocolList &ProtList, + const ObjCContainerDecl *ContD) { + ObjCInterfaceDecl::protocol_loc_iterator LI = ProtList.loc_begin(); + for (ObjCInterfaceDecl::protocol_iterator + I = ProtList.begin(), E = ProtList.end(); I != E; ++I, ++LI) { + SourceLocation Loc = *LI; + ObjCProtocolDecl *PD = *I; + TRY_TO(IndexCtx.handleReference(PD, Loc, ContD, ContD, + SymbolRoleSet(), + SymbolRelation{(unsigned)SymbolRole::RelationBaseOf, ContD})); + } + return true; + } + bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *D) { if (D->isThisDeclarationADefinition()) { - if (!IndexCtx.handleDecl(D)) - return false; - IndexCtx.indexDeclContext(D); + TRY_TO(IndexCtx.handleDecl(D)); + if (auto *SuperD = D->getSuperClass()) { + TRY_TO(IndexCtx.handleReference(SuperD, D->getSuperClassLoc(), D, D, + SymbolRoleSet(), + SymbolRelation{(unsigned)SymbolRole::RelationBaseOf, D})); + } + TRY_TO(handleReferencedProtocols(D->getReferencedProtocols(), D)); + TRY_TO(IndexCtx.indexDeclContext(D)); } else { return IndexCtx.handleReference(D, D->getLocation(), nullptr, nullptr, SymbolRoleSet()); @@ -210,9 +235,9 @@ class IndexingDeclVisitor : public ConstDeclVisitor { bool VisitObjCProtocolDecl(const ObjCProtocolDecl *D) { if (D->isThisDeclarationADefinition()) { - if (!IndexCtx.handleDecl(D)) - return false; - IndexCtx.indexDeclContext(D); + TRY_TO(IndexCtx.handleDecl(D)); + TRY_TO(handleReferencedProtocols(D->getReferencedProtocols(), D)); + TRY_TO(IndexCtx.indexDeclContext(D)); } else { return IndexCtx.handleReference(D, D->getLocation(), nullptr, nullptr, SymbolRoleSet()); diff --git a/test/Index/Core/index-source.m b/test/Index/Core/index-source.m index c2604f67545..766b6b198f2 100644 --- a/test/Index/Core/index-source.m +++ b/test/Index/Core/index-source.m @@ -18,3 +18,23 @@ void goo(Base *b) { // CHECK-NEXT: RelRec | Base | c:objc(cs)Base [b meth]; } + +// CHECK: [[@LINE+1]]:11 | objc-protocol/ObjC | Prot1 | c:objc(pl)Prot1 | | Decl | rel: 0 +@protocol Prot1 +@end + +// CHECK: [[@LINE+3]]:11 | objc-protocol/ObjC | Prot2 | c:objc(pl)Prot2 | | Decl | rel: 0 +// CHECK: [[@LINE+2]]:17 | objc-protocol/ObjC | Prot1 | c:objc(pl)Prot1 | | Ref,RelBase | rel: 1 +// CHECK-NEXT: RelBase | Prot2 | c:objc(pl)Prot2 +@protocol Prot2 +@end + +// CHECK: [[@LINE+7]]:12 | objc-class/ObjC | Sub | c:objc(cs)Sub | _OBJC_CLASS_$_Sub | Decl | rel: 0 +// CHECK: [[@LINE+6]]:18 | objc-class/ObjC | Base | c:objc(cs)Base | _OBJC_CLASS_$_Base | Ref,RelBase | rel: 1 +// CHECK-NEXT: RelBase | Sub | c:objc(cs)Sub +// CHECK: [[@LINE+4]]:23 | objc-protocol/ObjC | Prot2 | c:objc(pl)Prot2 | | Ref,RelBase | rel: 1 +// CHECK-NEXT: RelBase | Sub | c:objc(cs)Sub +// CHECK: [[@LINE+2]]:30 | objc-protocol/ObjC | Prot1 | c:objc(pl)Prot1 | | Ref,RelBase | rel: 1 +// CHECK-NEXT: RelBase | Sub | c:objc(cs)Sub +@interface Sub : Base +@end From 1b7c3afcb81447df9eadfd37551c8c7ceb30e31f Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Thu, 3 Mar 2016 21:38:39 +0000 Subject: [PATCH 257/742] [analyzer] ObjCDeallocChecker: Only check for nil-out when type is retainable. This fixes a crash when setting a property of struct type in -dealloc. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262659 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 5d64047903eb9f048cd8eaf3586ade3a7a51b61d) --- .../Checkers/CheckObjCDealloc.cpp | 6 +++++- test/Analysis/DeallocMissingRelease.m | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index c09924cfc2e..f9fd9fcf957 100644 --- a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -860,9 +860,13 @@ ObjCDeallocChecker::getValueReleasedByNillingOut(const ObjCMethodCall &M, if (!ReceiverVal.isValid()) return nullptr; - // Is the first argument nil? if (M.getNumArgs() == 0) return nullptr; + + if (!M.getArgExpr(0)->getType()->isObjCRetainableType()) + return nullptr; + + // Is the first argument nil? SVal Arg = M.getArgSVal(0); ProgramStateRef notNilState, nilState; std::tie(notNilState, nilState) = diff --git a/test/Analysis/DeallocMissingRelease.m b/test/Analysis/DeallocMissingRelease.m index 383bacb539f..75afd0e5f1b 100644 --- a/test/Analysis/DeallocMissingRelease.m +++ b/test/Analysis/DeallocMissingRelease.m @@ -664,6 +664,25 @@ - (void)dealloc { @end #endif +struct SomeStruct { + int f; +}; +@interface ZeroOutStructWithSetter : NSObject + @property(assign) struct SomeStruct s; +@end + +@implementation ZeroOutStructWithSetter +- (void)dealloc { + struct SomeStruct zeroedS; + zeroedS.f = 0; + + self.s = zeroedS; +#if NON_ARC + [super dealloc]; +#endif +} +@end + #if NON_ARC @interface ReleaseIvarInArray : NSObject { NSObject *_array[3]; From f0150cfe12210d5e682fa010e9e832b619eb6514 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Fri, 4 Mar 2016 04:24:32 +0000 Subject: [PATCH 258/742] [index] Ignore ObjCTypeParamDecls during indexing. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262686 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Index/IndexingContext.cpp | 3 +++ test/Index/Core/index-source.m | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/lib/Index/IndexingContext.cpp b/lib/Index/IndexingContext.cpp index 1645a9ab0bd..87bedd778ae 100644 --- a/lib/Index/IndexingContext.cpp +++ b/lib/Index/IndexingContext.cpp @@ -103,6 +103,9 @@ bool IndexingContext::isFunctionLocalDecl(const Decl *D) { if (isa(D)) return true; + if (isa(D)) + return true; + if (!D->getParentFunctionOrMethod()) return false; diff --git a/test/Index/Core/index-source.m b/test/Index/Core/index-source.m index 766b6b198f2..d57879c8988 100644 --- a/test/Index/Core/index-source.m +++ b/test/Index/Core/index-source.m @@ -38,3 +38,8 @@ @protocol Prot2 // CHECK-NEXT: RelBase | Sub | c:objc(cs)Sub @interface Sub : Base @end + +@interface NSArray : Base +// CHECK-NOT: ObjectType +-(ObjectType)getit; +@end From caa011eb4fbc506affa357c3a74d7eb530f4d407 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Fri, 4 Mar 2016 07:17:43 +0000 Subject: [PATCH 259/742] [index] In ObjC++ handle objc type parameters for function USRs. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262693 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Index/USRGeneration.cpp | 17 +++++++++++++++++ test/Index/Core/index-source.mm | 11 +++++++++++ 2 files changed, 28 insertions(+) create mode 100644 test/Index/Core/index-source.mm diff --git a/lib/Index/USRGeneration.cpp b/lib/Index/USRGeneration.cpp index 3dfc27df42f..e5ff1751489 100644 --- a/lib/Index/USRGeneration.cpp +++ b/lib/Index/USRGeneration.cpp @@ -659,6 +659,11 @@ void USRGenerator::VisitType(QualType T) { T = PT->getPointeeType(); continue; } + if (const ObjCObjectPointerType *OPT = T->getAs()) { + Out << '*'; + T = OPT->getPointeeType(); + continue; + } if (const RValueReferenceType *RT = T->getAs()) { Out << "&&"; T = RT->getPointeeType(); @@ -693,6 +698,18 @@ void USRGenerator::VisitType(QualType T) { VisitTagDecl(TT->getDecl()); return; } + if (const ObjCInterfaceType *OIT = T->getAs()) { + Out << '$'; + VisitObjCInterfaceDecl(OIT->getDecl()); + return; + } + if (const ObjCObjectType *OIT = T->getAs()) { + Out << 'Q'; + VisitType(OIT->getBaseType()); + for (auto *Prot : OIT->getProtocols()) + VisitObjCProtocolDecl(Prot); + return; + } if (const TemplateTypeParmType *TTP = T->getAs()) { Out << 't' << TTP->getDepth() << '.' << TTP->getIndex(); return; diff --git a/test/Index/Core/index-source.mm b/test/Index/Core/index-source.mm new file mode 100644 index 00000000000..049a0bdaf64 --- /dev/null +++ b/test/Index/Core/index-source.mm @@ -0,0 +1,11 @@ +// RUN: c-index-test core -print-source-symbols -- %s -target x86_64-apple-macosx10.7 | FileCheck %s + +@interface MyCls +@end + +@protocol P1,P2; + +// CHECK: [[@LINE+1]]:6 | function/C | foo | c:@F@foo#*$objc(cs)MyCls# | __Z3fooP5MyCls | Decl | rel: 0 +void foo(MyCls *o); +// CHECK: [[@LINE+1]]:6 | function/C | foo | c:@F@foo#*Qoobjc(pl)P1objc(pl)P2# | __Z3fooPU15objcproto2P12P211objc_object | Decl | rel: 0 +void foo(id o); From 3b83581ddca810f431a428fc38faf7a6de256ab8 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Fri, 4 Mar 2016 07:17:48 +0000 Subject: [PATCH 260/742] [index] Include parameter types in the USRs for C functions marked with 'overloadable' attribute. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262694 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Index/USRGeneration.cpp | 3 ++- test/Index/Core/index-source.m | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/Index/USRGeneration.cpp b/lib/Index/USRGeneration.cpp index e5ff1751489..6249c54a67d 100644 --- a/lib/Index/USRGeneration.cpp +++ b/lib/Index/USRGeneration.cpp @@ -211,7 +211,8 @@ void USRGenerator::VisitFunctionDecl(const FunctionDecl *D) { D->getDeclName().print(Out, Policy); ASTContext &Ctx = *Context; - if (!Ctx.getLangOpts().CPlusPlus || D->isExternC()) + if ((!Ctx.getLangOpts().CPlusPlus || D->isExternC()) && + !D->hasAttr()) return; if (const TemplateArgumentList * diff --git a/test/Index/Core/index-source.m b/test/Index/Core/index-source.m index d57879c8988..d1326624630 100644 --- a/test/Index/Core/index-source.m +++ b/test/Index/Core/index-source.m @@ -43,3 +43,8 @@ @interface NSArray : Base // CHECK-NOT: ObjectType -(ObjectType)getit; @end + +// CHECK: [[@LINE+1]]:6 | function/C | over_func | c:@F@over_func#I# | __Z9over_funci | Decl | rel: 0 +void over_func(int x) __attribute__((overloadable)); +// CHECK: [[@LINE+1]]:6 | function/C | over_func | c:@F@over_func#f# | __Z9over_funcf | Decl | rel: 0 +void over_func(float x) __attribute__((overloadable)); From 2555fc6a9df56cf54a253a7ff03de7e7acaa2029 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Fri, 4 Mar 2016 07:17:53 +0000 Subject: [PATCH 261/742] [index] Distinguish USRs of anonymous enums by using their first enumerator. rdar://24609949. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262695 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Index/IndexingContext.cpp | 4 ---- lib/Index/USRGeneration.cpp | 13 +++++++++++-- test/Index/Core/index-source.m | 17 +++++++++++++++++ test/Index/usrs.m | 12 ++++++------ 4 files changed, 34 insertions(+), 12 deletions(-) diff --git a/lib/Index/IndexingContext.cpp b/lib/Index/IndexingContext.cpp index 87bedd778ae..204e4300f87 100644 --- a/lib/Index/IndexingContext.cpp +++ b/lib/Index/IndexingContext.cpp @@ -206,10 +206,6 @@ static const Decl *adjustParent(const Decl *Parent) { if (auto NS = dyn_cast(Parent)) { if (NS->isAnonymousNamespace()) continue; - } else if (auto EnumD = dyn_cast(Parent)) { - // Move enumerators under anonymous enum to the enclosing parent. - if (EnumD->getDeclName().isEmpty()) - continue; } else if (auto RD = dyn_cast(Parent)) { if (RD->isAnonymousStructOrUnion()) continue; diff --git a/lib/Index/USRGeneration.cpp b/lib/Index/USRGeneration.cpp index 6249c54a67d..86873f27e2d 100644 --- a/lib/Index/USRGeneration.cpp +++ b/lib/Index/USRGeneration.cpp @@ -421,7 +421,8 @@ void USRGenerator::VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *D) { void USRGenerator::VisitTagDecl(const TagDecl *D) { // Add the location of the tag decl to handle resolution across // translation units. - if (ShouldGenerateLocation(D) && GenLoc(D, /*IncludeOffset=*/isLocal(D))) + if (!isa(D) && + ShouldGenerateLocation(D) && GenLoc(D, /*IncludeOffset=*/isLocal(D))) return; D = D->getCanonicalDecl(); @@ -477,8 +478,16 @@ void USRGenerator::VisitTagDecl(const TagDecl *D) { else { if (D->isEmbeddedInDeclarator() && !D->isFreeStanding()) { printLoc(Out, D->getLocation(), Context->getSourceManager(), true); - } else + } else { Buf[off] = 'a'; + if (auto *ED = dyn_cast(D)) { + // Distinguish USRs of anonymous enums by using their first enumerator. + auto enum_range = ED->enumerators(); + if (enum_range.begin() != enum_range.end()) { + Out << '@' << **enum_range.begin(); + } + } + } } } diff --git a/test/Index/Core/index-source.m b/test/Index/Core/index-source.m index d1326624630..d70974ca034 100644 --- a/test/Index/Core/index-source.m +++ b/test/Index/Core/index-source.m @@ -48,3 +48,20 @@ -(ObjectType)getit; void over_func(int x) __attribute__((overloadable)); // CHECK: [[@LINE+1]]:6 | function/C | over_func | c:@F@over_func#f# | __Z9over_funcf | Decl | rel: 0 void over_func(float x) __attribute__((overloadable)); + +// CHECK: [[@LINE+1]]:6 | enum/C | MyEnum | c:@E@MyEnum | | Def | rel: 0 +enum MyEnum { + // CHECK: [[@LINE+2]]:3 | enumerator/C | EnumeratorInNamed | c:@E@MyEnum@EnumeratorInNamed | | Def,RelChild | rel: 1 + // CHECK-NEXT: RelChild | MyEnum | c:@E@MyEnum + EnumeratorInNamed +}; + +// CHECK: [[@LINE+1]]:1 | enum/C | | c:@Ea@One | | Def | rel: 0 +enum { + // CHECK: [[@LINE+2]]:3 | enumerator/C | One | c:@Ea@One@One | | Def,RelChild | rel: 1 + // CHECK-NEXT: RelChild | | c:@Ea@One + One, + // CHECK: [[@LINE+2]]:3 | enumerator/C | Two | c:@Ea@One@Two | | Def,RelChild | rel: 1 + // CHECK-NEXT: RelChild | | c:@Ea@One + Two, +}; diff --git a/test/Index/usrs.m b/test/Index/usrs.m index fc3fbc91057..92c3a3fafee 100644 --- a/test/Index/usrs.m +++ b/test/Index/usrs.m @@ -110,12 +110,12 @@ -(int)methodWithFn:(void (*)(int *p))fn; // CHECK: usrs.m c:usrs.m@F@my_helper Extent=[3:1 - 3:60] // CHECK: usrs.m c:usrs.m@95@F@my_helper@x Extent=[3:29 - 3:34] // CHECK: usrs.m c:usrs.m@102@F@my_helper@y Extent=[3:36 - 3:41] -// CHECK: usrs.m c:usrs.m@Ea Extent=[5:1 - 8:2] -// CHECK: usrs.m c:usrs.m@Ea@ABA Extent=[6:3 - 6:6] -// CHECK: usrs.m c:usrs.m@Ea@CADABA Extent=[7:3 - 7:9] -// CHECK: usrs.m c:usrs.m@Ea Extent=[10:1 - 13:2] -// CHECK: usrs.m c:usrs.m@Ea@FOO Extent=[11:3 - 11:6] -// CHECK: usrs.m c:usrs.m@Ea@BAR Extent=[12:3 - 12:6] +// CHECK: usrs.m c:@Ea@ABA Extent=[5:1 - 8:2] +// CHECK: usrs.m c:@Ea@ABA@ABA Extent=[6:3 - 6:6] +// CHECK: usrs.m c:@Ea@ABA@CADABA Extent=[7:3 - 7:9] +// CHECK: usrs.m c:@Ea@FOO Extent=[10:1 - 13:2] +// CHECK: usrs.m c:@Ea@FOO@FOO Extent=[11:3 - 11:6] +// CHECK: usrs.m c:@Ea@FOO@BAR Extent=[12:3 - 12:6] // CHECK: usrs.m c:@SA@MyStruct Extent=[15:9 - 18:2] // CHECK: usrs.m c:@SA@MyStruct@FI@wa Extent=[16:3 - 16:9] // CHECK: usrs.m c:@SA@MyStruct@FI@moo Extent=[17:3 - 17:10] From e1d180b7900714dc2dd05f13158d37c62250d14f Mon Sep 17 00:00:00 2001 From: Vedant Kumar Date: Fri, 4 Mar 2016 08:07:15 +0000 Subject: [PATCH 262/742] [Coverage] Fix the start/end locations of switch statements While pushing switch statements onto the region stack we neglected to specify their start/end locations. This results in a crash (PR26825) if we end up in nested macro expansions without enough information to handle the relevant file exits. I added a test in switchmacro.c and fixed up a bunch of incorrect CHECK lines that specify strange end locations for switches. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262697 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit f377a655e7e305e24ee4bf8fa007e2d0558f96d9) --- lib/CodeGen/CoverageMappingGen.cpp | 2 +- test/CoverageMapping/switch.c | 24 ++++++++++++------------ test/CoverageMapping/switchmacro.c | 14 ++++++++++++-- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/lib/CodeGen/CoverageMappingGen.cpp b/lib/CodeGen/CoverageMappingGen.cpp index 9279d1a37cf..adafee43bf8 100644 --- a/lib/CodeGen/CoverageMappingGen.cpp +++ b/lib/CodeGen/CoverageMappingGen.cpp @@ -776,7 +776,7 @@ struct CounterCoverageMappingBuilder BreakContinueStack.back().ContinueCount, BC.ContinueCount); Counter ExitCount = getRegionCounter(S); - pushRegion(ExitCount); + pushRegion(ExitCount, getStart(S), getEnd(S)); } void VisitSwitchCase(const SwitchCase *S) { diff --git a/test/CoverageMapping/switch.c b/test/CoverageMapping/switch.c index 3c0b0323f69..72b08520eee 100644 --- a/test/CoverageMapping/switch.c +++ b/test/CoverageMapping/switch.c @@ -1,44 +1,44 @@ // RUN: %clang_cc1 -fprofile-instr-generate -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only -main-file-name switch.c %s | FileCheck %s // CHECK: foo void foo(int i) { // CHECK-NEXT: File 0, [[@LINE]]:17 -> [[@LINE+8]]:2 = #0 - switch(i) { + switch(i) { // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+5]]:4 = #1 case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+3]]:10 = #2 return; case 2: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:10 = #3 break; } - int x = 0; // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:2 = #1 + int x = 0; } void nop() {} // CHECK: bar void bar(int i) { // CHECK-NEXT: File 0, [[@LINE]]:17 -> [[@LINE+20]]:2 = #0 - switch (i) + switch (i) // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:6 = #1 ; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:6 = 0 - switch (i) { // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+16]]:2 = #1 + switch (i) { // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:4 = #2 } - switch (i) // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+13]]:2 = #2 + switch (i) // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:10 = #3 nop(); // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:10 = 0 - switch (i) // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+10]]:2 = #3 + switch (i) // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+2]]:10 = #4 case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:10 = #5 nop(); - switch (i) { // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+6]]:2 = #4 + switch (i) { // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+4]]:4 = #6 nop(); // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE+2]]:10 = 0 case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:10 = #7 nop(); } - nop(); // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:2 = #6 + nop(); } // CHECK-NEXT: main int main() { // CHECK-NEXT: File 0, [[@LINE]]:12 -> [[@LINE+34]]:2 = #0 int i = 0; - switch(i) { + switch(i) { // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+9]]:4 = #1 case 0: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+7]]:10 = #2 i = 1; break; @@ -48,7 +48,7 @@ int main() { // CHECK-NEXT: File 0, [[@LINE]]:12 -> [[@LINE+34]]:2 = #0 default: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:10 = #4 break; } - switch(i) { // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+22]]:2 = #1 + switch(i) { // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+8]]:4 = #5 case 0: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+6]]:10 = #6 i = 1; break; @@ -58,7 +58,7 @@ int main() { // CHECK-NEXT: File 0, [[@LINE]]:12 -> [[@LINE+34]]:2 = #0 break; } - switch(i) { // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+12]]:2 = #5 + switch(i) { // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+7]]:4 = #9 case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+5]]:11 = #10 case 2: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+4]]:11 = (#10 + #11) i = 11; @@ -67,7 +67,7 @@ int main() { // CHECK-NEXT: File 0, [[@LINE]]:12 -> [[@LINE+34]]:2 = #0 i = 99; } - foo(1); // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+2]]:11 = #9 + foo(1); bar(1); return 0; } diff --git a/test/CoverageMapping/switchmacro.c b/test/CoverageMapping/switchmacro.c index 431d5c72ea5..5f6f51a11c5 100644 --- a/test/CoverageMapping/switchmacro.c +++ b/test/CoverageMapping/switchmacro.c @@ -4,11 +4,11 @@ // CHECK: foo int foo(int i) { // CHECK-NEXT: File 0, [[@LINE]]:16 -> {{[0-9]+}}:2 = #0 - switch (i) { + switch (i) { // CHECK-NEXT: File 0, [[@LINE]]:3 -> {{[0-9]+}}:4 = #1 default: // CHECK-NEXT: File 0, [[@LINE]]:3 -> {{[0-9]+}}:11 = #2 if (i == 1) // CHECK-NEXT: File 0, [[@LINE]]:9 -> [[@LINE]]:15 = #2 return 0; // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE]]:15 = #3 - // CHECK-NEXT: Expansion,File 0, [[@LINE+2]]:5 -> [[@LINE+2]]:8 = (#2 - #3) + // CHECK-NEXT: Expansion,File 0, [[@LINE+2]]:5 -> [[@LINE+2]]:8 = (#2 - #3) (Expanded file = 1) // CHECK-NEXT: File 0, [[@LINE+1]]:8 -> {{[0-9]+}}:11 = (#2 - #3) FOO(1); case 0: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:13 = ((#2 + #4) - #3) @@ -22,6 +22,16 @@ int foo(int i) { // CHECK-NEXT: File 0, [[@LINE]]:16 -> {{[0-9]+}}:2 = #0 } } +// PR26825 - Crash when exiting macro expansion containing a switch +// CHECK: bar +#define START { while (0) { switch (0) { +#define END }}} +void bar() { + START // CHECK: File 0, [[@LINE]]:8 -> [[@LINE+2]]:6 +default: ; + END +} + int main(int argc, const char *argv[]) { foo(3); return 0; From 5a705a66e0570d1df6631b97a2f97e182c83b98a Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Fri, 4 Mar 2016 16:06:20 -0800 Subject: [PATCH 263/742] Fixup test typos in the swift_name tests --- test/SemaObjC/attr-swift.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/SemaObjC/attr-swift.m b/test/SemaObjC/attr-swift.m index 28042635f75..effcfefff2f 100644 --- a/test/SemaObjC/attr-swift.m +++ b/test/SemaObjC/attr-swift.m @@ -98,8 +98,8 @@ struct __attribute__((swift_name("FooStruct"))) BarStruct { typedef int some_int_type __attribute__((swift_name("SomeInt"))); struct Point3D createPoint3D(float x, float y, float z) __attribute__((swift_name("Point3D.init(x:y:z:)"))); -struct Point3D rotatePoint3D(Point3D point, float radians) __attribute__((swift_name("Point3D.rotate(self:radius:)"))); -struct Point3D badRotatePoint3D(Point3D point, float radians) __attribute__((swift_name("Point3D.rotate(radius:)"))); // expected-warning {{too few parameters in 'swift_name' attribute (expected 2; got 1)}} +struct Point3D rotatePoint3D(Point3D point, float radians) __attribute__((swift_name("Point3D.rotate(self:radians:)"))); +struct Point3D badRotatePoint3D(Point3D point, float radians) __attribute__((swift_name("Point3D.rotate(radians:)"))); // expected-warning {{too few parameters in 'swift_name' attribute (expected 2; got 1)}} extern struct Point3D identityPoint __attribute__((swift_name("Point3D.identity"))); @@ -117,8 +117,8 @@ struct __attribute__((swift_name("FooStruct"))) BarStruct { void setLastPoint3D(Point3D point) __attribute__((swift_name("setter:lastPoint3D(_:)"))); -Point3D getZeroPoint() __attribute__((swift_name("getter:Point3D()"))); -void setZeroPoint(Point3D point) __attribute__((swift_name("setter:Point3D(_:)"))); +Point3D getZeroPoint() __attribute__((swift_name("getter:Point3D.zero()"))); +void setZeroPoint(Point3D point) __attribute__((swift_name("setter:Point3D.zero(_:)"))); Point3D badGetter1(int x) __attribute__((swift_name("getter:bad1(_:))"))); // expected-error{{parameter of 'swift_name' attribute must be a Swift function name string}} void badSetter1() __attribute__((swift_name("getter:bad1())"))); // expected-error{{parameter of 'swift_name' attribute must be a Swift function name string}} From 07ed0e22f17295ad22c9142d580b2b7b62e34394 Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Fri, 4 Mar 2016 16:06:20 -0800 Subject: [PATCH 264/742] Fixup test typos in the swift_name tests --- test/SemaObjC/attr-swift.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/SemaObjC/attr-swift.m b/test/SemaObjC/attr-swift.m index 28042635f75..effcfefff2f 100644 --- a/test/SemaObjC/attr-swift.m +++ b/test/SemaObjC/attr-swift.m @@ -98,8 +98,8 @@ struct __attribute__((swift_name("FooStruct"))) BarStruct { typedef int some_int_type __attribute__((swift_name("SomeInt"))); struct Point3D createPoint3D(float x, float y, float z) __attribute__((swift_name("Point3D.init(x:y:z:)"))); -struct Point3D rotatePoint3D(Point3D point, float radians) __attribute__((swift_name("Point3D.rotate(self:radius:)"))); -struct Point3D badRotatePoint3D(Point3D point, float radians) __attribute__((swift_name("Point3D.rotate(radius:)"))); // expected-warning {{too few parameters in 'swift_name' attribute (expected 2; got 1)}} +struct Point3D rotatePoint3D(Point3D point, float radians) __attribute__((swift_name("Point3D.rotate(self:radians:)"))); +struct Point3D badRotatePoint3D(Point3D point, float radians) __attribute__((swift_name("Point3D.rotate(radians:)"))); // expected-warning {{too few parameters in 'swift_name' attribute (expected 2; got 1)}} extern struct Point3D identityPoint __attribute__((swift_name("Point3D.identity"))); @@ -117,8 +117,8 @@ struct __attribute__((swift_name("FooStruct"))) BarStruct { void setLastPoint3D(Point3D point) __attribute__((swift_name("setter:lastPoint3D(_:)"))); -Point3D getZeroPoint() __attribute__((swift_name("getter:Point3D()"))); -void setZeroPoint(Point3D point) __attribute__((swift_name("setter:Point3D(_:)"))); +Point3D getZeroPoint() __attribute__((swift_name("getter:Point3D.zero()"))); +void setZeroPoint(Point3D point) __attribute__((swift_name("setter:Point3D.zero(_:)"))); Point3D badGetter1(int x) __attribute__((swift_name("getter:bad1(_:))"))); // expected-error{{parameter of 'swift_name' attribute must be a Swift function name string}} void badSetter1() __attribute__((swift_name("getter:bad1())"))); // expected-error{{parameter of 'swift_name' attribute must be a Swift function name string}} From 86dfdce5e7dd87a5167e9c16f22da1cc9439d451 Mon Sep 17 00:00:00 2001 From: Benjamin Kramer Date: Fri, 4 Mar 2016 14:18:52 +0000 Subject: [PATCH 265/742] Move class into anonymous namespace. NFC. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262716 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit e0bd794efa508ad8b2c7a95f6f7091c9cbe54593) --- lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp index eb31321895f..15980c5c538 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp @@ -61,6 +61,7 @@ class ObjCSuperDeallocChecker // SymbolRef for the receiver. REGISTER_SET_WITH_PROGRAMSTATE(CalledSuperDealloc, SymbolRef) +namespace { class SuperDeallocBRVisitor final : public BugReporterVisitorImpl { @@ -81,6 +82,7 @@ class SuperDeallocBRVisitor final ID.Add(ReceiverSymbol); } }; +} // End anonymous namespace. void ObjCSuperDeallocChecker::checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const { From a2b118efa7db6f39a7c8fed0a1e64f52e4a09911 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Fri, 4 Mar 2016 18:09:58 +0000 Subject: [PATCH 266/742] [analyzer] Add diagnostic in ObjCDeallocChecker for use of -dealloc instead of -release. In dealloc methods, the analyzer now warns when -dealloc is called directly on a synthesized retain/copy ivar instead of -release. This is intended to find mistakes of the form: - (void)dealloc { [_ivar dealloc]; // Mistaken call to -dealloc instead of -release [super dealloc]; } rdar://problem/16227989 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262729 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 0c35e452895ad039178f3a19e90a1c36b4554749) --- .../Checkers/CheckObjCDealloc.cpp | 124 +++++++++++++----- test/Analysis/DeallocMissingRelease.m | 21 +++ 2 files changed, 113 insertions(+), 32 deletions(-) diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index f9fd9fcf957..84856bd6dcd 100644 --- a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -103,6 +103,7 @@ class ObjCDeallocChecker std::unique_ptr MissingReleaseBugType; std::unique_ptr ExtraReleaseBugType; + std::unique_ptr MistakenDeallocBugType; public: ObjCDeallocChecker(); @@ -130,14 +131,20 @@ class ObjCDeallocChecker bool diagnoseExtraRelease(SymbolRef ReleasedValue, const ObjCMethodCall &M, CheckerContext &C) const; - SymbolRef getValueExplicitlyReleased(const ObjCMethodCall &M, - CheckerContext &C) const; + bool diagnoseMistakenDealloc(SymbolRef DeallocedValue, + const ObjCMethodCall &M, + CheckerContext &C) const; + SymbolRef getValueReleasedByNillingOut(const ObjCMethodCall &M, CheckerContext &C) const; const ObjCIvarRegion *getIvarRegionForIvarSymbol(SymbolRef IvarSym) const; SymbolRef getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym) const; + const ObjCPropertyImplDecl* + findPropertyOnDeallocatingInstance(SymbolRef IvarSym, + CheckerContext &C) const; + ReleaseRequirement getDeallocReleaseRequirement(const ObjCPropertyImplDecl *PropImpl) const; @@ -336,7 +343,14 @@ void ObjCDeallocChecker::checkPreObjCMessage( if (!instanceDeallocIsOnStack(C, DeallocedInstance)) return; - SymbolRef ReleasedValue = getValueExplicitlyReleased(M, C); + SymbolRef ReleasedValue = nullptr; + + if (M.getSelector() == ReleaseSel) { + ReleasedValue = M.getReceiverSVal().getAsSymbol(); + } else if (M.getSelector() == DeallocSel && !M.isReceiverSelfOrSuper()) { + if (diagnoseMistakenDealloc(M.getReceiverSVal().getAsSymbol(), M, C)) + return; + } if (ReleasedValue) { // An instance variable symbol was released with -release: @@ -597,40 +611,55 @@ void ObjCDeallocChecker::diagnoseMissingReleases(CheckerContext &C) const { assert(!LCtx->inTopFrame() || State->get().isEmpty()); } +/// Given a symbol, determine whether the symbol refers to an ivar on +/// the top-most deallocating instance. If so, find the property for that +/// ivar, if one exists. Otherwise return null. +const ObjCPropertyImplDecl * +ObjCDeallocChecker::findPropertyOnDeallocatingInstance( + SymbolRef IvarSym, CheckerContext &C) const { + SVal DeallocedInstance; + if (!isInInstanceDealloc(C, DeallocedInstance)) + return nullptr; + + // Try to get the region from which the ivar value was loaded. + auto *IvarRegion = getIvarRegionForIvarSymbol(IvarSym); + if (!IvarRegion) + return nullptr; + + // Don't try to find the property if the ivar was not loaded from the + // given instance. + if (DeallocedInstance.castAs().getRegion() != + IvarRegion->getSuperRegion()) + return nullptr; + + const LocationContext *LCtx = C.getLocationContext(); + const ObjCIvarDecl *IvarDecl = IvarRegion->getDecl(); + + const ObjCImplDecl *Container = getContainingObjCImpl(LCtx); + const ObjCPropertyImplDecl *PropImpl = + Container->FindPropertyImplIvarDecl(IvarDecl->getIdentifier()); + return PropImpl; +} + /// Emits a warning if the current context is -dealloc and ReleasedValue /// must not be directly released in a -dealloc. Returns true if a diagnostic /// was emitted. bool ObjCDeallocChecker::diagnoseExtraRelease(SymbolRef ReleasedValue, const ObjCMethodCall &M, CheckerContext &C) const { - SVal DeallocedInstance; - if (!isInInstanceDealloc(C, DeallocedInstance)) - return false; - // Try to get the region from which the the released value was loaded. // Note that, unlike diagnosing for missing releases, here we don't track // values that must not be released in the state. This is because even if // these values escape, it is still an error under the rules of MRR to // release them in -dealloc. - auto *ReleasedIvar = getIvarRegionForIvarSymbol(ReleasedValue); - if (!ReleasedIvar) - return false; + const ObjCPropertyImplDecl *PropImpl = + findPropertyOnDeallocatingInstance(ReleasedValue, C); - if (DeallocedInstance.castAs().getRegion() != - ReleasedIvar->getSuperRegion()) + if (!PropImpl) return false; - const LocationContext *LCtx = C.getLocationContext(); - const ObjCIvarDecl *ReleasedIvarDecl = ReleasedIvar->getDecl(); - // If the ivar belongs to a property that must not be released directly // in dealloc, emit a warning. - const ObjCImplDecl *Container = getContainingObjCImpl(LCtx); - const ObjCPropertyImplDecl *PropImpl = - Container->FindPropertyImplIvarDecl(ReleasedIvarDecl->getIdentifier()); - if (!PropImpl) - return false; - if (getDeallocReleaseRequirement(PropImpl) != ReleaseRequirement::MustNotReleaseDirectly) { return false; @@ -661,6 +690,7 @@ bool ObjCDeallocChecker::diagnoseExtraRelease(SymbolRef ReleasedValue, (PropDecl->getSetterKind() == ObjCPropertyDecl::Assign && !PropDecl->isReadOnly())); + const ObjCImplDecl *Container = getContainingObjCImpl(C.getLocationContext()); OS << "The '" << *PropImpl->getPropertyIvarDecl() << "' ivar in '" << *Container << "' was synthesized for "; @@ -681,6 +711,43 @@ bool ObjCDeallocChecker::diagnoseExtraRelease(SymbolRef ReleasedValue, return true; } +/// Emits a warning if the current context is -dealloc and DeallocedValue +/// must not be directly dealloced in a -dealloc. Returns true if a diagnostic +/// was emitted. +bool ObjCDeallocChecker::diagnoseMistakenDealloc(SymbolRef DeallocedValue, + const ObjCMethodCall &M, + CheckerContext &C) const { + + // Find the property backing the instance variable that M + // is dealloc'ing. + const ObjCPropertyImplDecl *PropImpl = + findPropertyOnDeallocatingInstance(DeallocedValue, C); + if (!PropImpl) + return false; + + if (getDeallocReleaseRequirement(PropImpl) != + ReleaseRequirement::MustRelease) { + return false; + } + + ExplodedNode *ErrNode = C.generateErrorNode(); + if (!ErrNode) + return false; + + std::string Buf; + llvm::raw_string_ostream OS(Buf); + + OS << "'" << *PropImpl->getPropertyIvarDecl() + << "' should be released rather than deallocated"; + + std::unique_ptr BR( + new BugReport(*MistakenDeallocBugType, OS.str(), ErrNode)); + BR->addRange(M.getOriginExpr()->getSourceRange()); + + C.emitReport(std::move(BR)); + + return true; +} ObjCDeallocChecker:: ObjCDeallocChecker() @@ -693,6 +760,10 @@ ObjCDeallocChecker:: ExtraReleaseBugType.reset( new BugType(this, "Extra ivar release", categories::MemoryCoreFoundationObjectiveC)); + + MistakenDeallocBugType.reset( + new BugType(this, "Mistaken dealloc", + categories::MemoryCoreFoundationObjectiveC)); } void ObjCDeallocChecker::initIdentifierInfoAndSelectors( @@ -840,17 +911,6 @@ ReleaseRequirement ObjCDeallocChecker::getDeallocReleaseRequirement( llvm_unreachable("Unrecognized setter kind"); } -/// Returns the released value if M is a call to -release. Returns -/// nullptr otherwise. -SymbolRef -ObjCDeallocChecker::getValueExplicitlyReleased(const ObjCMethodCall &M, - CheckerContext &C) const { - if (M.getSelector() != ReleaseSel) - return nullptr; - - return M.getReceiverSVal().getAsSymbol(); -} - /// Returns the released value if M is a call a setter that releases /// and nils out its underlying instance variable. SymbolRef diff --git a/test/Analysis/DeallocMissingRelease.m b/test/Analysis/DeallocMissingRelease.m index 75afd0e5f1b..26f32db7ff0 100644 --- a/test/Analysis/DeallocMissingRelease.m +++ b/test/Analysis/DeallocMissingRelease.m @@ -733,3 +733,24 @@ -(void)dealloc; { } @end +// Warn about calling -dealloc rather than release by mistake. + +@interface CallDeallocOnRetainPropIvar : NSObject { + NSObject *okToDeallocDirectly; +} + +@property (retain) NSObject *ivar; +@end + +@implementation CallDeallocOnRetainPropIvar +- (void)dealloc +{ +#if NON_ARC + // Only warn for synthesized ivars. + [okToDeallocDirectly dealloc]; // now-warning + [_ivar dealloc]; // expected-warning {{'_ivar' should be released rather than deallocated}} + + [super dealloc]; +#endif +} +@end From 9c7704996e0dbd13fdf4ce29e3fe5821b3258e39 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Sat, 5 Mar 2016 01:32:43 +0000 Subject: [PATCH 267/742] [analyzer] Nullability: add option to not report on calls to system headers. Add an -analyzer-config 'nullability:NoDiagnoseCallsToSystemHeaders' option to the nullability checker. When enabled, this option causes the analyzer to not report about passing null/nullable values to functions and methods declared in system headers. This option is motivated by the observation that large projects may have many nullability warnings. These projects may find warnings about nullability annotations that they have explicitly added themselves higher priority to fix than warnings on calls to system libraries. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262763 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit e608712133a5c27b1a47f7127074670c9d61aeb8) --- .../Checkers/NullabilityChecker.cpp | 27 ++++++++- .../system-header-simulator-for-nullability.h | 42 ++++++++++++++ test/Analysis/nullability.mm | 58 ++++++++----------- test/Analysis/nullability_nullonly.mm | 41 ++++++++----- 4 files changed, 116 insertions(+), 52 deletions(-) create mode 100644 test/Analysis/Inputs/system-header-simulator-for-nullability.h diff --git a/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp index e096e2047f5..31560f2687c 100644 --- a/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp @@ -100,6 +100,14 @@ class NullabilityChecker mutable std::unique_ptr BT; public: + // If true, the checker will not diagnose nullabilility issues for calls + // to system headers. This option is motivated by the observation that large + // projects may have many nullability warnings. These projects may + // find warnings about nullability annotations that they have explicitly + // added themselves higher priority to fix than warnings on calls to system + // libraries. + DefaultBool NoDiagnoseCallsToSystemHeaders; + void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const; void checkPostStmt(const ExplicitCastExpr *CE, CheckerContext &C) const; void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; @@ -191,6 +199,15 @@ class NullabilityChecker /// to the wrapped region. Otherwise it will return a nullptr. const SymbolicRegion *getTrackRegion(SVal Val, bool CheckSuperRegion = false) const; + + /// Returns true if the call is diagnosable in the currrent analyzer + /// configuration. + bool isDiagnosableCall(const CallEvent &Call) const { + if (NoDiagnoseCallsToSystemHeaders && Call.isInSystemHeader()) + return false; + + return true; + } }; class NullabilityState { @@ -620,7 +637,8 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call, if (Filter.CheckNullPassedToNonnull && Nullness == NullConstraint::IsNull && ArgExprTypeLevelNullability != Nullability::Nonnull && - RequiredNullability == Nullability::Nonnull) { + RequiredNullability == Nullability::Nonnull && + isDiagnosableCall(Call)) { ExplodedNode *N = C.generateErrorNode(State); if (!N) return; @@ -647,7 +665,8 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call, continue; if (Filter.CheckNullablePassedToNonnull && - RequiredNullability == Nullability::Nonnull) { + RequiredNullability == Nullability::Nonnull && + isDiagnosableCall(Call)) { ExplodedNode *N = C.addTransition(State); SmallString<256> SBuf; llvm::raw_svector_ostream OS(SBuf); @@ -1106,6 +1125,10 @@ void NullabilityChecker::printState(raw_ostream &Out, ProgramStateRef State, checker->Filter.Check##name = true; \ checker->Filter.CheckName##name = mgr.getCurrentCheckName(); \ checker->NeedTracking = checker->NeedTracking || trackingRequired; \ + checker->NoDiagnoseCallsToSystemHeaders = \ + checker->NoDiagnoseCallsToSystemHeaders || \ + mgr.getAnalyzerOptions().getBooleanOption( \ + "NoDiagnoseCallsToSystemHeaders", false, checker, true); \ } // The checks are likely to be turned on by default and it is possible to do diff --git a/test/Analysis/Inputs/system-header-simulator-for-nullability.h b/test/Analysis/Inputs/system-header-simulator-for-nullability.h new file mode 100644 index 00000000000..9bb2786bd7f --- /dev/null +++ b/test/Analysis/Inputs/system-header-simulator-for-nullability.h @@ -0,0 +1,42 @@ +#pragma clang system_header + +#define nil 0 +#define BOOL int + +#define NS_ASSUME_NONNULL_BEGIN _Pragma("clang assume_nonnull begin") +#define NS_ASSUME_NONNULL_END _Pragma("clang assume_nonnull end") + +NS_ASSUME_NONNULL_BEGIN + +typedef struct _NSZone NSZone; + +@protocol NSObject ++ (id)alloc; +- (id)init; +@end + +@protocol NSCopying +- (id)copyWithZone:(nullable NSZone *)zone; +@end + +@protocol NSMutableCopying +- (id)mutableCopyWithZone:(nullable NSZone *)zone; +@end + +__attribute__((objc_root_class)) +@interface +NSObject +@end + +@interface NSString : NSObject +- (BOOL)isEqualToString : (NSString *)aString; +- (NSString *)stringByAppendingString:(NSString *)aString; +@end + +void NSSystemFunctionTakingNonnull(NSString *s); + +@interface NSSystemClass : NSObject +- (void) takesNonnull:(NSString *)s; +@end + +NS_ASSUME_NONNULL_END diff --git a/test/Analysis/nullability.mm b/test/Analysis/nullability.mm index 220a38118aa..0a3ae7a1968 100644 --- a/test/Analysis/nullability.mm +++ b/test/Analysis/nullability.mm @@ -1,38 +1,7 @@ -// RUN: %clang_cc1 -fblocks -analyze -analyzer-checker=core,nullability -verify %s +// RUN: %clang_cc1 -fblocks -analyze -analyzer-checker=core,nullability -DNOSYSTEMHEADERS=0 -verify %s +// RUN: %clang_cc1 -fblocks -analyze -analyzer-checker=core,nullability -analyzer-config nullability:NoDiagnoseCallsToSystemHeaders=true -DNOSYSTEMHEADERS=1 -verify %s -#define nil 0 -#define BOOL int - -#define NS_ASSUME_NONNULL_BEGIN _Pragma("clang assume_nonnull begin") -#define NS_ASSUME_NONNULL_END _Pragma("clang assume_nonnull end") - -typedef struct _NSZone NSZone; - -@protocol NSObject -+ (id)alloc; -- (id)init; -@end - -NS_ASSUME_NONNULL_BEGIN -@protocol NSCopying -- (id)copyWithZone:(nullable NSZone *)zone; - -@end - -@protocol NSMutableCopying -- (id)mutableCopyWithZone:(nullable NSZone *)zone; -@end -NS_ASSUME_NONNULL_END - -__attribute__((objc_root_class)) -@interface -NSObject -@end - -@interface NSString : NSObject -- (BOOL)isEqualToString : (NSString *_Nonnull)aString; -- (NSString *)stringByAppendingString:(NSString *_Nonnull)aString; -@end +#include "Inputs/system-header-simulator-for-nullability.h" @interface TestObject : NSObject - (int *_Nonnull)returnsNonnull; @@ -419,3 +388,24 @@ -(id)mutableCopyWithZone:(NSZone *)zone { return newInstance; } @end + +NSString * _Nullable returnsNullableString(); + +void callFunctionInSystemHeader() { + NSString *s = returnsNullableString(); + + NSSystemFunctionTakingNonnull(s); + #if !NOSYSTEMHEADERS + // expected-warning@-2{{Nullable pointer is passed to a callee that requires a non-null 1st parameter}} + #endif +} + +void callMethodInSystemHeader() { + NSString *s = returnsNullableString(); + + NSSystemClass *sc = [[NSSystemClass alloc] init]; + [sc takesNonnull:s]; + #if !NOSYSTEMHEADERS + // expected-warning@-2{{Nullable pointer is passed to a callee that requires a non-null 1st parameter}} + #endif +} diff --git a/test/Analysis/nullability_nullonly.mm b/test/Analysis/nullability_nullonly.mm index d82105cf5b5..9671877719f 100644 --- a/test/Analysis/nullability_nullonly.mm +++ b/test/Analysis/nullability_nullonly.mm @@ -1,20 +1,7 @@ -// RUN: %clang_cc1 -analyze -fobjc-arc -analyzer-checker=core,nullability.NullPassedToNonnull,nullability.NullReturnedFromNonnull -verify %s +// RUN: %clang_cc1 -analyze -fobjc-arc -analyzer-checker=core,nullability.NullPassedToNonnull,nullability.NullReturnedFromNonnull -DNOSYSTEMHEADERS=0 -verify %s +// RUN: %clang_cc1 -analyze -fobjc-arc -analyzer-checker=core,nullability.NullPassedToNonnull,nullability.NullReturnedFromNonnull -analyzer-config nullability:NoDiagnoseCallsToSystemHeaders=true -DNOSYSTEMHEADERS=1 -verify %s -#define nil 0 -#define BOOL int - -@protocol NSObject -+ (id)alloc; -- (id)init; -@end - -@protocol NSCopying -@end - -__attribute__((objc_root_class)) -@interface -NSObject -@end +#include "Inputs/system-header-simulator-for-nullability.h" int getRandom(); @@ -159,3 +146,25 @@ - (SomeClass * _Nonnull)testReturnsNilInNonnullWhenPreconditionViolated:(SomeCla return p; // no-warning } @end + + +void callFunctionInSystemHeader() { + NSString *s; + s = nil; + + NSSystemFunctionTakingNonnull(s); + #if !NOSYSTEMHEADERS + // expected-warning@-2{{Null passed to a callee that requires a non-null 1st parameter}} + #endif +} + +void callMethodInSystemHeader() { + NSString *s; + s = nil; + + NSSystemClass *sc = [[NSSystemClass alloc] init]; + [sc takesNonnull:s]; + #if !NOSYSTEMHEADERS + // expected-warning@-2{{Null passed to a callee that requires a non-null 1st parameter}} + #endif +} From bea015e454d29265849a481c3131d1b93f93613f Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Mon, 7 Mar 2016 20:58:52 +0000 Subject: [PATCH 268/742] Module Debugging: Fix a crash when emitting debug info for nested tag types whose DeclContext is not yet complete by deferring their emission. rdar://problem/24918680 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262851 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 45b0585fa2e7c9c467a938ce8838c9b3ceb01ca8) --- lib/CodeGen/ObjectFilePCHContainerOperations.cpp | 9 +++++++++ test/Modules/Inputs/DebugCXX.h | 11 +++++++++++ test/Modules/ModuleDebugInfo.cpp | 7 +++++++ 3 files changed, 27 insertions(+) diff --git a/lib/CodeGen/ObjectFilePCHContainerOperations.cpp b/lib/CodeGen/ObjectFilePCHContainerOperations.cpp index 14c30dec755..d560cb1a80f 100644 --- a/lib/CodeGen/ObjectFilePCHContainerOperations.cpp +++ b/lib/CodeGen/ObjectFilePCHContainerOperations.cpp @@ -201,6 +201,15 @@ class PCHContainerGenerator : public ASTConsumer { if (D->getName().empty()) return; + // Defer tag decls until their declcontext is complete. + auto *DeclCtx = D->getDeclContext(); + while (DeclCtx) { + if (auto *D = dyn_cast(DeclCtx)) + if (!D->isCompleteDefinition()) + return; + DeclCtx = DeclCtx->getParent(); + } + DebugTypeVisitor DTV(*Builder->getModuleDebugInfo(), *Ctx); DTV.TraverseDecl(D); Builder->UpdateCompletedType(D); diff --git a/test/Modules/Inputs/DebugCXX.h b/test/Modules/Inputs/DebugCXX.h index 39dda959ce2..2e17a2ecfde 100644 --- a/test/Modules/Inputs/DebugCXX.h +++ b/test/Modules/Inputs/DebugCXX.h @@ -72,3 +72,14 @@ namespace { struct InAnonymousNamespace { int i; }; } } + +class Base; +class A { + virtual Base *getParent() const; +}; +class Base {}; +class Derived : Base { + class B : A { + Derived *getParent() const override; + }; +}; diff --git a/test/Modules/ModuleDebugInfo.cpp b/test/Modules/ModuleDebugInfo.cpp index 73443972409..993e72a7706 100644 --- a/test/Modules/ModuleDebugInfo.cpp +++ b/test/Modules/ModuleDebugInfo.cpp @@ -68,6 +68,13 @@ // CHECK-SAME-NOT: name: // CHECK-SAME: identifier: "_ZTS13TypedefStruct") +// CHECK: !DICompositeType(tag: DW_TAG_class_type, name: "Derived", +// CHECK-SAME: identifier: "_ZTS7Derived") +// CHECK: !DICompositeType(tag: DW_TAG_class_type, name: "B", scope: !"_ZTS7Derived", +// CHECK-SAME: elements: ![[B_MBRS:.*]], vtableHolder: !"_ZTS1A" +// CHECK: ![[B_MBRS]] = !{{{.*}}, ![[GET_PARENT:.*]]} +// CHECK: ![[GET_PARENT]] = !DISubprogram(name: "getParent" + // CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "FloatInstatiation" // no mangled name here yet. From cd4a33ed4af8940f2ce53ea342273828e5850d70 Mon Sep 17 00:00:00 2001 From: Bob Wilson Date: Sat, 13 Feb 2016 01:41:41 +0000 Subject: [PATCH 269/742] [Sema] More changes to fix Objective-C fallout from r249995. This is a follow-up to PR26085. That was fixed in r257710 but the testcase there was incomplete. There is a related issue where the overload resolution for Objective-C incorrectly picks a method that is not valid without a bridge cast. The call to Sema::CheckSingleAssignmentConstraints that was added to SemaOverload.cpp's IsStandardConversion() function does not catch that case and reports that the method is Compatible even when it is not. The root cause here is that various Objective-C-related functions in Sema do not consistently return a value to indicate whether there was an error. This was fine in the past because they would report diagnostics when needed, but r257710 changed them to suppress reporting diagnostics when checking during overload resolution. This patch adds a new ACR_error result to the ARCConversionResult enum and updates Sema::CheckObjCARCConversion to return that value when there is an error. Most of the calls to that function do not check the return value, so adding this new result does not affect them. The one exception is in SemaCast.cpp where it specifically checks for ACR_unbridged, so that is also OK. The call in Sema::CheckSingleAssignmentConstraints can then check for an ACR_okay result and identify assignments as Incompatible. To preserve the existing behavior, it only changes the return value to Incompatible when the new Diagnose argument (from r257710) is false. Similarly, the CheckObjCBridgeRelatedConversions and ConversionToObjCStringLiteralCheck need to identify when an assignment is Incompatible. Those functions already return appropriate values but they need some fixes related to the new Diagnose argument. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260787 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Sema/Sema.h | 2 +- lib/Sema/SemaExpr.cpp | 22 ++++++++++---- lib/Sema/SemaExprObjC.cpp | 62 +++++++++++++++++++++------------------ test/SemaObjC/ovl-check.m | 13 ++++---- 4 files changed, 60 insertions(+), 39 deletions(-) diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 54328efddd8..1ccd027ca71 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -8637,7 +8637,7 @@ class Sema { Expr *CastExpr, SourceLocation RParenLoc); - enum ARCConversionResult { ACR_okay, ACR_unbridged }; + enum ARCConversionResult { ACR_okay, ACR_unbridged, ACR_error }; /// \brief Checks for invalid conversions and casts between /// retainable pointers and other pointer kinds. diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 68ceac2ae62..f51ed3817d4 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -7560,13 +7560,24 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS, if (result != Incompatible && RHS.get()->getType() != LHSType) { QualType Ty = LHSType.getNonLValueExprType(Context); Expr *E = RHS.get(); - if (getLangOpts().ObjCAutoRefCount) - CheckObjCARCConversion(SourceRange(), Ty, E, CCK_ImplicitConversion, - Diagnose, DiagnoseCFAudited); + + // Check for various Objective-C errors. If we are not reporting + // diagnostics and just checking for errors, e.g., during overload + // resolution, return Incompatible to indicate the failure. + if (getLangOpts().ObjCAutoRefCount && + CheckObjCARCConversion(SourceRange(), Ty, E, CCK_ImplicitConversion, + Diagnose, DiagnoseCFAudited) != ACR_okay) { + if (!Diagnose) + return Incompatible; + } if (getLangOpts().ObjC1 && (CheckObjCBridgeRelatedConversions(E->getLocStart(), LHSType, E->getType(), E, Diagnose) || ConversionToObjCStringLiteralCheck(LHSType, E, Diagnose))) { + if (!Diagnose) + return Incompatible; + // Replace the expression with a corrected version and continue so we + // can find further errors. RHS = E; return Compatible; } @@ -12021,10 +12032,11 @@ bool Sema::ConversionToObjCStringLiteralCheck(QualType DstType, Expr *&Exp, StringLiteral *SL = dyn_cast(SrcExpr); if (!SL || !SL->isAscii()) return false; - if (Diagnose) + if (Diagnose) { Diag(SL->getLocStart(), diag::err_missing_atsign_prefix) << FixItHint::CreateInsertion(SL->getLocStart(), "@"); - Exp = BuildObjCStringLiteral(SL->getLocStart(), SL).get(); + Exp = BuildObjCStringLiteral(SL->getLocStart(), SL).get(); + } return true; } diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp index 53f39e3b2a3..ab812fc1dfe 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/lib/Sema/SemaExprObjC.cpp @@ -3478,6 +3478,8 @@ diagnoseObjCARCConversion(Sema &S, SourceRange castRange, return; QualType castExprType = castExpr->getType(); + // Defer emitting a diagnostic for bridge-related casts; that will be + // handled by CheckObjCBridgeRelatedConversions. TypedefNameDecl *TDNDecl = nullptr; if ((castACTC == ACTC_coreFoundation && exprACTC == ACTC_retainable && ObjCBridgeRelatedAttrFromType(castType, TDNDecl)) || @@ -3922,16 +3924,16 @@ Sema::CheckObjCBridgeRelatedConversions(SourceLocation Loc, << FixItHint::CreateInsertion(SrcExprEndLoc, "]"); Diag(RelatedClass->getLocStart(), diag::note_declared_at); Diag(TDNDecl->getLocStart(), diag::note_declared_at); - } - QualType receiverType = Context.getObjCInterfaceType(RelatedClass); - // Argument. - Expr *args[] = { SrcExpr }; - ExprResult msg = BuildClassMessageImplicit(receiverType, false, + QualType receiverType = Context.getObjCInterfaceType(RelatedClass); + // Argument. + Expr *args[] = { SrcExpr }; + ExprResult msg = BuildClassMessageImplicit(receiverType, false, ClassMethod->getLocation(), ClassMethod->getSelector(), ClassMethod, MultiExprArg(args, 1)); - SrcExpr = msg.get(); + SrcExpr = msg.get(); + } return true; } } @@ -3965,14 +3967,14 @@ Sema::CheckObjCBridgeRelatedConversions(SourceLocation Loc, } Diag(RelatedClass->getLocStart(), diag::note_declared_at); Diag(TDNDecl->getLocStart(), diag::note_declared_at); - } - ExprResult msg = - BuildInstanceMessageImplicit(SrcExpr, SrcType, - InstanceMethod->getLocation(), - InstanceMethod->getSelector(), - InstanceMethod, None); - SrcExpr = msg.get(); + ExprResult msg = + BuildInstanceMessageImplicit(SrcExpr, SrcType, + InstanceMethod->getLocation(), + InstanceMethod->getSelector(), + InstanceMethod, None); + SrcExpr = msg.get(); + } return true; } } @@ -3996,9 +3998,9 @@ Sema::CheckObjCARCConversion(SourceRange castRange, QualType castType, ARCConversionTypeClass exprACTC = classifyTypeForARCConversion(castExprType); ARCConversionTypeClass castACTC = classifyTypeForARCConversion(effCastType); if (exprACTC == castACTC) { - // check for viablity and report error if casting an rvalue to a + // Check for viability and report error if casting an rvalue to a // life-time qualifier. - if (Diagnose && castACTC == ACTC_retainable && + if (castACTC == ACTC_retainable && (CCK == CCK_CStyleCast || CCK == CCK_OtherCast) && castType != castExprType) { const Type *DT = castType.getTypePtr(); @@ -4014,10 +4016,12 @@ Sema::CheckObjCARCConversion(SourceRange castRange, QualType castType, QDT = AT->desugar(); if (QDT != castType && QDT.getObjCLifetime() != Qualifiers::OCL_None) { - SourceLocation loc = - (castRange.isValid() ? castRange.getBegin() - : castExpr->getExprLoc()); - Diag(loc, diag::err_arc_nolifetime_behavior); + if (Diagnose) { + SourceLocation loc = (castRange.isValid() ? castRange.getBegin() + : castExpr->getExprLoc()); + Diag(loc, diag::err_arc_nolifetime_behavior); + } + return ACR_error; } } return ACR_okay; @@ -4065,24 +4069,26 @@ Sema::CheckObjCARCConversion(SourceRange castRange, QualType castType, CCK != CCK_ImplicitConversion) return ACR_unbridged; - // Do not issue bridge cast" diagnostic when implicit casting a cstring - // to 'NSString *'. Let caller issue a normal mismatched diagnostic with - // suitable fix-it. + // Issue a diagnostic about a missing @-sign when implicit casting a cstring + // to 'NSString *', instead of falling through to report a "bridge cast" + // diagnostic. if (castACTC == ACTC_retainable && exprACTC == ACTC_none && ConversionToObjCStringLiteralCheck(castType, castExpr, Diagnose)) - return ACR_okay; + return ACR_error; // Do not issue "bridge cast" diagnostic when implicit casting // a retainable object to a CF type parameter belonging to an audited // CF API function. Let caller issue a normal type mismatched diagnostic // instead. - if (Diagnose && - (!DiagnoseCFAudited || exprACTC != ACTC_retainable || - castACTC != ACTC_coreFoundation)) - if (!(exprACTC == ACTC_voidPtr && castACTC == ACTC_retainable && - (Opc == BO_NE || Opc == BO_EQ))) + if ((!DiagnoseCFAudited || exprACTC != ACTC_retainable || + castACTC != ACTC_coreFoundation) && + !(exprACTC == ACTC_voidPtr && castACTC == ACTC_retainable && + (Opc == BO_NE || Opc == BO_EQ))) { + if (Diagnose) diagnoseObjCARCConversion(*this, castRange, castType, castACTC, castExpr, castExpr, exprACTC, CCK); + return ACR_error; + } return ACR_okay; } diff --git a/test/SemaObjC/ovl-check.m b/test/SemaObjC/ovl-check.m index 7fc8a640491..cc2f094ad2e 100644 --- a/test/SemaObjC/ovl-check.m +++ b/test/SemaObjC/ovl-check.m @@ -4,20 +4,24 @@ // in overload resolution in ObjC. struct Type1 { int a; }; +typedef const __attribute__((objc_bridge(id))) void * CFTypeRef; @interface Iface1 @end @interface NeverCalled - (void) test:(struct Type1 *)arg; +- (void) test2:(CFTypeRef)arg; @end @interface TakesIface1 - (void) test:(Iface1 *)arg; +- (void) test2:(Iface1 *)arg; @end // PR26085, rdar://problem/24111333 void testTakesIface1(id x, Iface1 *arg) { // This should resolve silently to `TakesIface1`. [x test:arg]; + [x test2:arg]; } @class NSString; @@ -36,17 +40,16 @@ void testTakesNSString(id x) { [x testStr:"someStringLiteral"]; } -typedef const void *CFTypeRef; id CreateSomething(); -@interface NeverCalledv3 -- (void) testCFTypeRef:(struct Type1 *)arg; -@end - @interface TakesCFTypeRef - (void) testCFTypeRef:(CFTypeRef)arg; @end +@interface NeverCalledv3 +- (void) testCFTypeRef:(struct Type1 *)arg; +@end + // Not called out explicitly by PR26085, but related. void testTakesCFTypeRef(id x) { // Overload resolution should occur silently, select the CFTypeRef overload, From 706cc849154f8d0f2ab0bad1f2ee932965d33fdb Mon Sep 17 00:00:00 2001 From: "Duncan P. N. Exon Smith" Date: Tue, 8 Mar 2016 06:12:54 +0000 Subject: [PATCH 270/742] Sema: Treat 'strict' availability flag like unavailable This is a follow-up to r261512, which made the 'strict' availability attribute flag behave like 'unavailable'. However, that fix was insufficient. The following case would (erroneously) error when the deployment target was older than 10.9: struct __attribute__((availability(macosx,strict,introduced=10.9))) A; __attribute__((availability(macosx,strict,introduced=10.9))) void f(A*); The use of A* in the argument list for f is valid here, since f and A have the same availability. The fix is to return AR_Unavailable from DeclBase::getAvailability instead of AR_NotYetIntroduced. This also reverts the special handling added in r261163, instead relying on the well-tested logic for AR_Unavailable. rdar://problem/23791325 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262915 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit a1d201fccaf0e072dc2c70c2ecbc6c129087e3bd) --- lib/AST/DeclBase.cpp | 2 +- lib/Sema/SemaExpr.cpp | 10 +--------- test/Sema/attr-availability-macosx.c | 10 ++++++++++ 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index 128a5d08ab8..6cfe4d5b185 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -435,7 +435,7 @@ checkAvailability(ASTContext &Context, const AvailabilityAttr *A, << VTI << HintMessage; } - return AR_NotYetIntroduced; + return A->getStrict() ? AR_Unavailable : AR_NotYetIntroduced; } // Make sure that this declaration hasn't been obsoleted. diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index f51ed3817d4..a9fe9a0100b 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -159,19 +159,11 @@ DiagnoseAvailabilityOfDecl(Sema &S, NamedDecl *D, SourceLocation Loc, break; case AR_NotYetIntroduced: { - // With strict, the compiler will emit unavailable error. - AvailabilityAttr *AA = D->getAttr(); - if (AA && AA->getStrict() && - S.getCurContextAvailability() != AR_NotYetIntroduced) - S.EmitAvailabilityWarning(Sema::AD_Unavailable, - D, Message, Loc, UnknownObjCClass, ObjCPDecl, - ObjCPropertyAccess); - // Don't do this for enums, they can't be redeclared. if (isa(D) || isa(D)) break; - bool Warn = !AA->isInherited(); + bool Warn = !D->getAttr()->isInherited(); // Objective-C method declarations in categories are not modelled as // redeclarations, so manually look for a redeclaration in a category // if necessary. diff --git a/test/Sema/attr-availability-macosx.c b/test/Sema/attr-availability-macosx.c index 7efe7506926..21a79ff17a1 100644 --- a/test/Sema/attr-availability-macosx.c +++ b/test/Sema/attr-availability-macosx.c @@ -22,6 +22,16 @@ void test() { f6(0); // expected-error{{'f6' is unavailable: introduced in OS X 10.6}} } +struct __attribute__((availability(macosx,strict,introduced=10.6))) + not_yet_introduced_struct; // \ + expected-note{{'not_yet_introduced_struct' has been explicitly marked unavailable here}} + +void uses_not_introduced_struct(struct not_yet_introduced_struct *); // \ + expected-error{{'not_yet_introduced_struct' is unavailable: introduced in OS X 10.6}} + +__attribute__((availability(macosx,strict,introduced=10.6))) +void uses_not_introduced_struct_same_availability(struct not_yet_introduced_struct *); + // rdar://10535640 enum { From 4f75aaf421e181f108698bb9cd1cae79a691d9ce Mon Sep 17 00:00:00 2001 From: "Duncan P. N. Exon Smith" Date: Tue, 8 Mar 2016 10:28:52 +0000 Subject: [PATCH 271/742] Sema: Methods in unavailable classes are unavailable Similar to the template cases in r262050, when a C++ method in an unavailable struct/class calls unavailable API, don't diagnose an error. I.e., this case was failing: void foo() __attribute__((unavailable)); struct __attribute__((unavailable)) A { void bar() { foo(); } }; Since A is unavailable, A::bar is allowed to call foo. However, we were emitting a diagnostic here. This commit checks up the context chain from A::bar, in a manner inspired by SemaDeclAttr.cpp:isDeclUnavailable. I expected to find other related issues but failed to trigger them: - I wondered if DeclBase::getAvailability should check for `TemplateDecl` instead of `FunctionTemplateDecl`, but I couldn't find a way to trigger this. I left behind a few extra tests to make sure we don't regress. - I wondered if Sema::isFunctionConsideredUnavailable should be symmetric, checking up the context chain of the callee (this commit only checks up the context chain of the caller). However, I couldn't think of a testcase that didn't require first referencing the unavailable type; this, we already diagnose. rdar://problem/25030656 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262921 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 2b2f843fd0eed77baa55dd4371f14c1d68231572) --- lib/Sema/SemaOverload.cpp | 11 ++++++++++- test/SemaCXX/attr-unavailable.cpp | 23 +++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index eaac06162f7..7685fec3f26 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -1147,7 +1147,16 @@ bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old, /// \returns true if \arg FD is unavailable and current context is inside /// an available function, false otherwise. bool Sema::isFunctionConsideredUnavailable(FunctionDecl *FD) { - return FD->isUnavailable() && !cast(CurContext)->isUnavailable(); + if (!FD->isUnavailable()) + return false; + + // Walk up the context of the caller. + Decl *C = cast(CurContext); + do { + if (C->isUnavailable()) + return false; + } while ((C = cast_or_null(C->getDeclContext()))); + return true; } /// \brief Tries a user-defined conversion from From to ToType. diff --git a/test/SemaCXX/attr-unavailable.cpp b/test/SemaCXX/attr-unavailable.cpp index 430cb5cdb8c..bafae2ab43a 100644 --- a/test/SemaCXX/attr-unavailable.cpp +++ b/test/SemaCXX/attr-unavailable.cpp @@ -116,3 +116,26 @@ void calls_unavail_templated() { void unavail_calls_unavail_templated() __attribute__((unavailable)) { unavail_templated(5); } + +void unavailable() __attribute((unavailable)); // \ + expected-note 4{{candidate function has been explicitly made unavailable}} +struct AvailableStruct { + void calls_unavailable() { unavailable(); } // \ + expected-error{{call to unavailable function 'unavailable'}} + template void calls_unavailable() { unavailable(); } // \ + expected-error{{call to unavailable function 'unavailable'}} +}; +template struct AvailableStructTemplated { + void calls_unavailable() { unavailable(); } // \ + expected-error{{call to unavailable function 'unavailable'}} + template void calls_unavailable() { unavailable(); } // \ + expected-error{{call to unavailable function 'unavailable'}} +}; +struct __attribute__((unavailable)) UnavailableStruct { + void calls_unavailable() { unavailable(); } + template void calls_unavailable() { unavailable(); } +}; +template struct __attribute__((unavailable)) UnavailableStructTemplated { + void calls_unavailable() { unavailable(); } + template void calls_unavailable() { unavailable(); } +}; From 5c69f1757edb8f5620544545f415e6ea5dad136e Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Wed, 9 Mar 2016 02:12:40 +0000 Subject: [PATCH 272/742] [index] Fix assertion hit when indexing re-declarations of built-in functions. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262984 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Index/IndexingContext.cpp | 1 + test/Index/Core/index-source.m | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/lib/Index/IndexingContext.cpp b/lib/Index/IndexingContext.cpp index 204e4300f87..fe814ff25a6 100644 --- a/lib/Index/IndexingContext.cpp +++ b/lib/Index/IndexingContext.cpp @@ -298,6 +298,7 @@ bool IndexingContext::handleDeclOccurrence(const Decl *D, SourceLocation Loc, if (Parent) Parent = getCanonicalDecl(Parent); assert(!Parent || !Parent->isImplicit() || + isa(Parent) || isa(Parent) || isa(Parent)); SmallVector FinalRelations; diff --git a/test/Index/Core/index-source.m b/test/Index/Core/index-source.m index d70974ca034..6248f18bfb5 100644 --- a/test/Index/Core/index-source.m +++ b/test/Index/Core/index-source.m @@ -65,3 +65,8 @@ -(ObjectType)getit; // CHECK-NEXT: RelChild | | c:@Ea@One Two, }; + +// CHECK: [[@LINE+1]]:13 | typedef/C | jmp_buf | c:index-source.m@T@jmp_buf | | Def | rel: 0 +typedef int jmp_buf[(18)]; +// CHECK: [[@LINE+1]]:19 | typedef/C | jmp_buf | c:index-source.m@T@jmp_buf | | Ref | rel: 0 +extern int setjmp(jmp_buf); From 4c6ee9075666dfd18625877d30468128a98f60e9 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Wed, 9 Mar 2016 02:12:46 +0000 Subject: [PATCH 273/742] [index] libclang: Make sure to treat forward ObjC protocols as ObjCProtocolRef declarations, and fix related crash. rdar://25035376 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262985 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Index/IndexDecl.cpp | 8 ++++---- test/Index/index-refs.m | 5 +++++ tools/libclang/CXIndexDataConsumer.cpp | 8 ++++++++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/lib/Index/IndexDecl.cpp b/lib/Index/IndexDecl.cpp index af438f36843..5f5c49a2e33 100644 --- a/lib/Index/IndexDecl.cpp +++ b/lib/Index/IndexDecl.cpp @@ -227,8 +227,8 @@ class IndexingDeclVisitor : public ConstDeclVisitor { TRY_TO(handleReferencedProtocols(D->getReferencedProtocols(), D)); TRY_TO(IndexCtx.indexDeclContext(D)); } else { - return IndexCtx.handleReference(D, D->getLocation(), nullptr, nullptr, - SymbolRoleSet()); + return IndexCtx.handleReference(D, D->getLocation(), nullptr, + D->getDeclContext(), SymbolRoleSet()); } return true; } @@ -239,8 +239,8 @@ class IndexingDeclVisitor : public ConstDeclVisitor { TRY_TO(handleReferencedProtocols(D->getReferencedProtocols(), D)); TRY_TO(IndexCtx.indexDeclContext(D)); } else { - return IndexCtx.handleReference(D, D->getLocation(), nullptr, nullptr, - SymbolRoleSet()); + return IndexCtx.handleReference(D, D->getLocation(), nullptr, + D->getDeclContext(), SymbolRoleSet()); } return true; } diff --git a/test/Index/index-refs.m b/test/Index/index-refs.m index f25013b882f..457712bcbc7 100644 --- a/test/Index/index-refs.m +++ b/test/Index/index-refs.m @@ -21,7 +21,12 @@ void foo2() { [I clsMeth]; } +@protocol ForwardProt; + // RUN: c-index-test -index-file %s | FileCheck %s // CHECK: [indexEntityReference]: kind: objc-protocol | name: Prot | {{.*}} | loc: 12:27 // CHECK: [indexEntityReference]: kind: struct | name: FooS | {{.*}} | loc: 13:18 // CHECK: [indexEntityReference]: kind: objc-class | name: I | {{.*}} | loc: 21:4 + +// CHECK: [indexDeclaration]: kind: objc-protocol | name: ForwardProt | {{.*}} | loc: 24:11 +// CHECK-NEXT: : kind: forward-ref diff --git a/tools/libclang/CXIndexDataConsumer.cpp b/tools/libclang/CXIndexDataConsumer.cpp index 322725ec654..4f89e43fd7b 100644 --- a/tools/libclang/CXIndexDataConsumer.cpp +++ b/tools/libclang/CXIndexDataConsumer.cpp @@ -171,6 +171,14 @@ bool CXIndexDataConsumer::handleDeclOccurence(const Decl *D, return true; } } + if (auto *ObjCPD = dyn_cast_or_null(ASTNode.OrigD)) { + if (!ObjCPD->isThisDeclarationADefinition() && + ObjCPD->getLocation() == Loc) { + // The libclang API treats this as ObjCProtocolRef declaration. + IndexingDeclVisitor(*this, Loc, nullptr).Visit(ObjCPD); + return true; + } + } CXIdxEntityRefKind Kind = CXIdxEntityRef_Direct; if (Roles & (unsigned)SymbolRole::Implicit) { From ea9a01a14243bb3f3fb805e5338cb037871659e9 Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Wed, 9 Mar 2016 23:31:34 +0000 Subject: [PATCH 274/742] [Modules] Add stdatomic to the list of builtin headers Since it's provided by the compiler. This allows a system module map file to declare a module for it. No test change for cstd.m, since stdatomic.h doesn't function without a relatively complete stdint.h and stddef.h, which tests using this module don't provide. rdar://problem/24931246 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263076 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Lex/ModuleMap.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp index db8deab2fae..63a4813aa9f 100644 --- a/lib/Lex/ModuleMap.cpp +++ b/lib/Lex/ModuleMap.cpp @@ -154,6 +154,7 @@ static bool isBuiltinHeader(StringRef FileName) { .Case("limits.h", true) .Case("stdalign.h", true) .Case("stdarg.h", true) + .Case("stdatomic.h", true) .Case("stdbool.h", true) .Case("stddef.h", true) .Case("stdint.h", true) From 7ad2bb51b25af91ec4f856aba559b89c8cbdc92e Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Thu, 10 Mar 2016 23:51:03 +0000 Subject: [PATCH 275/742] Add has_feature objc_class_property. rdar://23891898 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263171 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Lex/PPMacroExpansion.cpp | 1 + test/SemaObjC/objc-class-property.m | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp index 9f3dc649a49..a25694930b1 100644 --- a/lib/Lex/PPMacroExpansion.cpp +++ b/lib/Lex/PPMacroExpansion.cpp @@ -1125,6 +1125,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .Case("objc_bridge_id_on_typedefs", true) .Case("objc_generics", LangOpts.ObjC2) .Case("objc_generics_variance", LangOpts.ObjC2) + .Case("objc_class_property", LangOpts.ObjC2) // C11 features .Case("c_alignas", LangOpts.C11) .Case("c_alignof", LangOpts.C11) diff --git a/test/SemaObjC/objc-class-property.m b/test/SemaObjC/objc-class-property.m index 77754400905..0058ee3648b 100644 --- a/test/SemaObjC/objc-class-property.m +++ b/test/SemaObjC/objc-class-property.m @@ -1,5 +1,9 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s +#if !__has_feature(objc_class_property) +#error does not support class property +#endif + @interface Root -(id) alloc; -(id) init; From cf057dcb80b523e14223f1816b55b41b2540d3ac Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 10 Mar 2016 23:03:57 -0800 Subject: [PATCH 276/742] [Swift] Add swift_bridge attribute for bridging Objective-C types to Swift. --- include/clang/APINotes/Types.h | 15 ++++++++++++++- include/clang/Basic/Attr.td | 8 ++++++++ include/clang/Basic/AttrDocs.td | 12 ++++++++++-- lib/APINotes/APINotesReader.cpp | 8 ++++++++ lib/APINotes/APINotesWriter.cpp | 7 ++++++- lib/APINotes/APINotesYAMLCompiler.cpp | 7 +++++++ lib/Sema/SemaAPINotes.cpp | 16 ++++++++++++++++ lib/Sema/SemaDeclAttr.cpp | 21 +++++++++++++++++++++ lib/Sema/SemaDeclObjC.cpp | 6 ++++-- test/APINotes/Inputs/roundtrip.apinotes | 2 ++ 10 files changed, 96 insertions(+), 6 deletions(-) diff --git a/include/clang/APINotes/Types.h b/include/clang/APINotes/Types.h index 79f34ccc218..decb137a0f5 100644 --- a/include/clang/APINotes/Types.h +++ b/include/clang/APINotes/Types.h @@ -127,6 +127,11 @@ class ObjCContextInfo : public CommonEntityInfo { /// Whether this class has designated initializers recorded. unsigned HasDesignatedInits : 1; + /// The Swift type to which a given Objective-C class is bridged. + /// + /// Reflects the swift_bridge attribute. + std::string SwiftBridge; + public: ObjCContextInfo() : CommonEntityInfo(), @@ -162,11 +167,15 @@ class ObjCContextInfo : public CommonEntityInfo { DefaultNullability = 0; } + const std::string &getSwiftBridge() const { return SwiftBridge; } + void setSwiftBridge(const std::string &swiftType) { SwiftBridge = swiftType; } + friend bool operator==(const ObjCContextInfo &lhs, const ObjCContextInfo &rhs) { return static_cast(lhs) == rhs && lhs.HasDefaultNullability == rhs.HasDefaultNullability && lhs.DefaultNullability == rhs.DefaultNullability && - lhs.HasDesignatedInits == rhs.HasDesignatedInits; + lhs.HasDesignatedInits == rhs.HasDesignatedInits && + lhs.SwiftBridge == rhs.SwiftBridge; } friend bool operator!=(const ObjCContextInfo &lhs, const ObjCContextInfo &rhs) { @@ -186,6 +195,10 @@ class ObjCContextInfo : public CommonEntityInfo { } lhs.HasDesignatedInits |= rhs.HasDesignatedInits; + + if (lhs.SwiftBridge.empty() && !rhs.SwiftBridge.empty()) + lhs.SwiftBridge = rhs.SwiftBridge; + return lhs; } diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 99be3afccf2..63748c38795 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -1309,6 +1309,14 @@ def Regparm : TypeAttr { let Documentation = [RegparmDocs]; } +def SwiftBridge : Attr { + let Spellings = [GNU<"swift_bridge">]; + let Subjects = SubjectList<[Tag, TypedefName, ObjCProtocol], ErrorDiag, + "ExpectedType">; + let Args = [StringArgument<"SwiftType">]; + let Documentation = [SwiftBridgeDocs]; +} + def SwiftError : InheritableAttr { let Spellings = [GCC<"swift_error">]; let Args = [EnumArgument<"Convention", "ConventionKind", diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td index 08cd99b9867..d27cdd9f92d 100644 --- a/include/clang/Basic/AttrDocs.td +++ b/include/clang/Basic/AttrDocs.td @@ -1758,6 +1758,12 @@ arguments, with arbitrary offsets. }]; } +def SwiftDocs : DocumentationCategory<"Controlling Swift Import"> { + let Content = [{ +Clang supports additional attributes for controlling how APIs are imported into Swift. + }]; +} + def NSErrorDomainDocs : Documentation { let Category = DocCatFunction; let Content = [{ @@ -1765,9 +1771,11 @@ The ``ns_error_domain`` attribute indicates a global constant representing the e }]; } -def SwiftDocs : DocumentationCategory<"Controlling Swift Import"> { + +def SwiftBridgeDocs : Documentation { + let Category = SwiftDocs; let Content = [{ -Clang supports additional attributes for controlling how APIs are imported into Swift. +The ``swift_bridge`` attribute indicates that the type to which the attribute appertains is bridged to the named Swift type. }]; } diff --git a/lib/APINotes/APINotesReader.cpp b/lib/APINotes/APINotesReader.cpp index 25831287c02..2d8fcb70690 100644 --- a/lib/APINotes/APINotesReader.cpp +++ b/lib/APINotes/APINotesReader.cpp @@ -140,6 +140,14 @@ namespace { } ++data; result.second.setHasDesignatedInits(*data++); + + // swift bridge. + unsigned swiftBridgeLength = + endian::readNext(data); + result.second.setSwiftBridge( + StringRef(reinterpret_cast(data), swiftBridgeLength)); + data += swiftBridgeLength; + return result; } }; diff --git a/lib/APINotes/APINotesWriter.cpp b/lib/APINotes/APINotesWriter.cpp index a9d83d95fd9..102932fdad0 100644 --- a/lib/APINotes/APINotesWriter.cpp +++ b/lib/APINotes/APINotesWriter.cpp @@ -302,7 +302,8 @@ namespace { uint32_t keyLength = sizeof(IdentifierID) + 1; uint32_t dataLength = sizeof(ContextID) + getCommonEntityInfoSize(data.second) - + dataBytes; + + dataBytes + + 2 + data.second.getSwiftBridge().size(); endian::Writer writer(out); writer.write(keyLength); writer.write(dataLength); @@ -333,6 +334,10 @@ namespace { bytes[2] = data.second.hasDesignatedInits(); out.write(reinterpret_cast(bytes), dataBytes); + + writer.write(data.second.getSwiftBridge().size()); + out.write(data.second.getSwiftBridge().data(), + data.second.getSwiftBridge().size()); } }; } // end anonymous namespace diff --git a/lib/APINotes/APINotesYAMLCompiler.cpp b/lib/APINotes/APINotesYAMLCompiler.cpp index 8896c40cda0..17d8fab3637 100644 --- a/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/lib/APINotes/APINotesYAMLCompiler.cpp @@ -189,6 +189,7 @@ namespace { bool AuditedForNullability = false; AvailabilityItem Availability; StringRef SwiftName; + StringRef SwiftBridge; MethodsSeq Methods; PropertiesSeq Properties; }; @@ -313,6 +314,7 @@ namespace llvm { io.mapOptional("Availability", c.Availability.Mode); io.mapOptional("AvailabilityMsg", c.Availability.Msg); io.mapOptional("SwiftName", c.SwiftName); + io.mapOptional("SwiftBridge", c.SwiftBridge); io.mapOptional("Methods", c.Methods); io.mapOptional("Properties", c.Properties); } @@ -521,6 +523,9 @@ static bool compile(const Module &module, if (cl.AuditedForNullability) cInfo.setDefaultNullability(*DefaultNullability); + if (isClass) + cInfo.setSwiftBridge(cl.SwiftBridge); + ContextID clID = isClass ? Writer->addObjCClass(cl.Name, cInfo) : Writer->addObjCProtocol(cl.Name, cInfo); @@ -727,6 +732,8 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, if (info.getDefaultNullability()) { record.AuditedForNullability = true; } + + record.SwiftBridge = copyString(info.getSwiftBridge()); } /// Map availability information, if present. diff --git a/lib/Sema/SemaAPINotes.cpp b/lib/Sema/SemaAPINotes.cpp index 4faabd093f8..22b29f4f24c 100644 --- a/lib/Sema/SemaAPINotes.cpp +++ b/lib/Sema/SemaAPINotes.cpp @@ -238,6 +238,22 @@ static void ProcessAPINotes(Sema &S, ObjCContainerDecl *D, ProcessAPINotes(S, D, static_cast(Info)); } +/// Process API notes for an Objective-C class. +static void ProcessAPINotes(Sema &S, ObjCInterfaceDecl *D, + const api_notes::ObjCContextInfo &Info) { + // swift_bridge + if (!Info.getSwiftBridge().empty() && + !D->getAttr()) { + D->addAttr( + SwiftBridgeAttr::CreateImplicit(S.Context, + CopyString(S.Context, + Info.getSwiftBridge()))); + } + + // Handle information common to Objective-C classes and protocols. + ProcessAPINotes(S, static_cast(D), Info); +} + /// Process API notes that are associated with this declaration, mapping them /// to attributes as appropriate. void Sema::ProcessAPINotes(Decl *D) { diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index b8bfc614f59..85fbc1f34d1 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -4715,6 +4715,24 @@ static void handleSwiftError(Sema &S, Decl *D, const AttributeList &attr) { attr.getAttributeSpellingListIndex())); } +static void handleSwiftBridgeAttr(Sema &S, Decl *D, const AttributeList &Attr) { + // Make sure that there is a string literal as the annotation's single + // argument. + StringRef Str; + if (!S.checkStringLiteralArgumentAttr(Attr, 0, Str)) + return; + + // Don't duplicate annotations that are already set. + if (D->hasAttr()) { + S.Diag(Attr.getLoc(), diag::warn_duplicate_attribute) << Attr.getName(); + return; + } + + D->addAttr(::new (S.Context) + SwiftBridgeAttr(Attr.getRange(), S.Context, Str, + Attr.getAttributeSpellingListIndex())); +} + //===----------------------------------------------------------------------===// // Microsoft specific attribute handlers. //===----------------------------------------------------------------------===// @@ -5914,6 +5932,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_SwiftError: handleSwiftError(S, D, Attr); break; + case AttributeList::AT_SwiftBridge: + handleSwiftBridgeAttr(S, D, Attr); + break; } } diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index 3f957af489e..e5db79f18e3 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -1687,7 +1687,8 @@ Sema::ActOnForwardProtocolDeclaration(SourceLocation AtProtocolLoc, = ObjCProtocolDecl::Create(Context, CurContext, Ident, IdentPair.second, AtProtocolLoc, PrevDecl); - + ProcessAPINotes(PDecl); + PushOnScopeChains(PDecl, TUScope); CheckObjCDeclScope(PDecl); @@ -3009,7 +3010,8 @@ Sema::ActOnForwardClassDeclaration(SourceLocation AtClassLoc, ClassName, TypeParams, PrevIDecl, IdentLocs[i]); IDecl->setAtEndRange(IdentLocs[i]); - + ProcessAPINotes(IDecl); + PushOnScopeChains(IDecl, TUScope); CheckObjCDeclScope(IDecl); DeclsInGroup.push_back(IDecl); diff --git a/test/APINotes/Inputs/roundtrip.apinotes b/test/APINotes/Inputs/roundtrip.apinotes index a1b60e5b19f..18bf2deeb19 100644 --- a/test/APINotes/Inputs/roundtrip.apinotes +++ b/test/APINotes/Inputs/roundtrip.apinotes @@ -7,6 +7,7 @@ Classes: Availability: available AvailabilityMsg: '' SwiftName: '' + SwiftBridge: '' Methods: - Selector: init MethodKind: Instance @@ -45,6 +46,7 @@ Classes: Availability: available AvailabilityMsg: '' SwiftName: '' + SwiftBridge: View Methods: - Selector: 'addSubview:' MethodKind: Instance From 6ce1bb86fbf908e266259563d968f15fe6a74eba Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 10 Mar 2016 23:03:57 -0800 Subject: [PATCH 277/742] [Swift] Add swift_bridge attribute for bridging Objective-C types to Swift. --- include/clang/APINotes/Types.h | 15 ++++++++++++++- include/clang/Basic/Attr.td | 8 ++++++++ include/clang/Basic/AttrDocs.td | 12 ++++++++++-- lib/APINotes/APINotesReader.cpp | 8 ++++++++ lib/APINotes/APINotesWriter.cpp | 7 ++++++- lib/APINotes/APINotesYAMLCompiler.cpp | 7 +++++++ lib/Sema/SemaAPINotes.cpp | 16 ++++++++++++++++ lib/Sema/SemaDeclAttr.cpp | 21 +++++++++++++++++++++ lib/Sema/SemaDeclObjC.cpp | 6 ++++-- test/APINotes/Inputs/roundtrip.apinotes | 2 ++ 10 files changed, 96 insertions(+), 6 deletions(-) diff --git a/include/clang/APINotes/Types.h b/include/clang/APINotes/Types.h index 79f34ccc218..decb137a0f5 100644 --- a/include/clang/APINotes/Types.h +++ b/include/clang/APINotes/Types.h @@ -127,6 +127,11 @@ class ObjCContextInfo : public CommonEntityInfo { /// Whether this class has designated initializers recorded. unsigned HasDesignatedInits : 1; + /// The Swift type to which a given Objective-C class is bridged. + /// + /// Reflects the swift_bridge attribute. + std::string SwiftBridge; + public: ObjCContextInfo() : CommonEntityInfo(), @@ -162,11 +167,15 @@ class ObjCContextInfo : public CommonEntityInfo { DefaultNullability = 0; } + const std::string &getSwiftBridge() const { return SwiftBridge; } + void setSwiftBridge(const std::string &swiftType) { SwiftBridge = swiftType; } + friend bool operator==(const ObjCContextInfo &lhs, const ObjCContextInfo &rhs) { return static_cast(lhs) == rhs && lhs.HasDefaultNullability == rhs.HasDefaultNullability && lhs.DefaultNullability == rhs.DefaultNullability && - lhs.HasDesignatedInits == rhs.HasDesignatedInits; + lhs.HasDesignatedInits == rhs.HasDesignatedInits && + lhs.SwiftBridge == rhs.SwiftBridge; } friend bool operator!=(const ObjCContextInfo &lhs, const ObjCContextInfo &rhs) { @@ -186,6 +195,10 @@ class ObjCContextInfo : public CommonEntityInfo { } lhs.HasDesignatedInits |= rhs.HasDesignatedInits; + + if (lhs.SwiftBridge.empty() && !rhs.SwiftBridge.empty()) + lhs.SwiftBridge = rhs.SwiftBridge; + return lhs; } diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 8ff03ce30d9..2a531e788ae 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -1310,6 +1310,14 @@ def Regparm : TypeAttr { let Documentation = [RegparmDocs]; } +def SwiftBridge : Attr { + let Spellings = [GNU<"swift_bridge">]; + let Subjects = SubjectList<[Tag, TypedefName, ObjCProtocol], ErrorDiag, + "ExpectedType">; + let Args = [StringArgument<"SwiftType">]; + let Documentation = [SwiftBridgeDocs]; +} + def SwiftError : InheritableAttr { let Spellings = [GCC<"swift_error">]; let Args = [EnumArgument<"Convention", "ConventionKind", diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td index dff98411962..676b0720a35 100644 --- a/include/clang/Basic/AttrDocs.td +++ b/include/clang/Basic/AttrDocs.td @@ -1764,6 +1764,12 @@ arguments, with arbitrary offsets. }]; } +def SwiftDocs : DocumentationCategory<"Controlling Swift Import"> { + let Content = [{ +Clang supports additional attributes for controlling how APIs are imported into Swift. + }]; +} + def NSErrorDomainDocs : Documentation { let Category = DocCatFunction; let Content = [{ @@ -1771,9 +1777,11 @@ The ``ns_error_domain`` attribute indicates a global constant representing the e }]; } -def SwiftDocs : DocumentationCategory<"Controlling Swift Import"> { + +def SwiftBridgeDocs : Documentation { + let Category = SwiftDocs; let Content = [{ -Clang supports additional attributes for controlling how APIs are imported into Swift. +The ``swift_bridge`` attribute indicates that the type to which the attribute appertains is bridged to the named Swift type. }]; } diff --git a/lib/APINotes/APINotesReader.cpp b/lib/APINotes/APINotesReader.cpp index 25831287c02..2d8fcb70690 100644 --- a/lib/APINotes/APINotesReader.cpp +++ b/lib/APINotes/APINotesReader.cpp @@ -140,6 +140,14 @@ namespace { } ++data; result.second.setHasDesignatedInits(*data++); + + // swift bridge. + unsigned swiftBridgeLength = + endian::readNext(data); + result.second.setSwiftBridge( + StringRef(reinterpret_cast(data), swiftBridgeLength)); + data += swiftBridgeLength; + return result; } }; diff --git a/lib/APINotes/APINotesWriter.cpp b/lib/APINotes/APINotesWriter.cpp index a9d83d95fd9..102932fdad0 100644 --- a/lib/APINotes/APINotesWriter.cpp +++ b/lib/APINotes/APINotesWriter.cpp @@ -302,7 +302,8 @@ namespace { uint32_t keyLength = sizeof(IdentifierID) + 1; uint32_t dataLength = sizeof(ContextID) + getCommonEntityInfoSize(data.second) - + dataBytes; + + dataBytes + + 2 + data.second.getSwiftBridge().size(); endian::Writer writer(out); writer.write(keyLength); writer.write(dataLength); @@ -333,6 +334,10 @@ namespace { bytes[2] = data.second.hasDesignatedInits(); out.write(reinterpret_cast(bytes), dataBytes); + + writer.write(data.second.getSwiftBridge().size()); + out.write(data.second.getSwiftBridge().data(), + data.second.getSwiftBridge().size()); } }; } // end anonymous namespace diff --git a/lib/APINotes/APINotesYAMLCompiler.cpp b/lib/APINotes/APINotesYAMLCompiler.cpp index 8896c40cda0..17d8fab3637 100644 --- a/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/lib/APINotes/APINotesYAMLCompiler.cpp @@ -189,6 +189,7 @@ namespace { bool AuditedForNullability = false; AvailabilityItem Availability; StringRef SwiftName; + StringRef SwiftBridge; MethodsSeq Methods; PropertiesSeq Properties; }; @@ -313,6 +314,7 @@ namespace llvm { io.mapOptional("Availability", c.Availability.Mode); io.mapOptional("AvailabilityMsg", c.Availability.Msg); io.mapOptional("SwiftName", c.SwiftName); + io.mapOptional("SwiftBridge", c.SwiftBridge); io.mapOptional("Methods", c.Methods); io.mapOptional("Properties", c.Properties); } @@ -521,6 +523,9 @@ static bool compile(const Module &module, if (cl.AuditedForNullability) cInfo.setDefaultNullability(*DefaultNullability); + if (isClass) + cInfo.setSwiftBridge(cl.SwiftBridge); + ContextID clID = isClass ? Writer->addObjCClass(cl.Name, cInfo) : Writer->addObjCProtocol(cl.Name, cInfo); @@ -727,6 +732,8 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, if (info.getDefaultNullability()) { record.AuditedForNullability = true; } + + record.SwiftBridge = copyString(info.getSwiftBridge()); } /// Map availability information, if present. diff --git a/lib/Sema/SemaAPINotes.cpp b/lib/Sema/SemaAPINotes.cpp index 649e8c45597..b9dcfb87341 100644 --- a/lib/Sema/SemaAPINotes.cpp +++ b/lib/Sema/SemaAPINotes.cpp @@ -239,6 +239,22 @@ static void ProcessAPINotes(Sema &S, ObjCContainerDecl *D, ProcessAPINotes(S, D, static_cast(Info)); } +/// Process API notes for an Objective-C class. +static void ProcessAPINotes(Sema &S, ObjCInterfaceDecl *D, + const api_notes::ObjCContextInfo &Info) { + // swift_bridge + if (!Info.getSwiftBridge().empty() && + !D->getAttr()) { + D->addAttr( + SwiftBridgeAttr::CreateImplicit(S.Context, + CopyString(S.Context, + Info.getSwiftBridge()))); + } + + // Handle information common to Objective-C classes and protocols. + ProcessAPINotes(S, static_cast(D), Info); +} + /// Process API notes that are associated with this declaration, mapping them /// to attributes as appropriate. void Sema::ProcessAPINotes(Decl *D) { diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 79da40f2341..29e828f6bb9 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -4720,6 +4720,24 @@ static void handleSwiftError(Sema &S, Decl *D, const AttributeList &attr) { attr.getAttributeSpellingListIndex())); } +static void handleSwiftBridgeAttr(Sema &S, Decl *D, const AttributeList &Attr) { + // Make sure that there is a string literal as the annotation's single + // argument. + StringRef Str; + if (!S.checkStringLiteralArgumentAttr(Attr, 0, Str)) + return; + + // Don't duplicate annotations that are already set. + if (D->hasAttr()) { + S.Diag(Attr.getLoc(), diag::warn_duplicate_attribute) << Attr.getName(); + return; + } + + D->addAttr(::new (S.Context) + SwiftBridgeAttr(Attr.getRange(), S.Context, Str, + Attr.getAttributeSpellingListIndex())); +} + //===----------------------------------------------------------------------===// // Microsoft specific attribute handlers. //===----------------------------------------------------------------------===// @@ -5919,6 +5937,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_SwiftError: handleSwiftError(S, D, Attr); break; + case AttributeList::AT_SwiftBridge: + handleSwiftBridgeAttr(S, D, Attr); + break; } } diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index 9ed1777a715..ff90c9e3a96 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -1687,7 +1687,8 @@ Sema::ActOnForwardProtocolDeclaration(SourceLocation AtProtocolLoc, = ObjCProtocolDecl::Create(Context, CurContext, Ident, IdentPair.second, AtProtocolLoc, PrevDecl); - + ProcessAPINotes(PDecl); + PushOnScopeChains(PDecl, TUScope); CheckObjCDeclScope(PDecl); @@ -3009,7 +3010,8 @@ Sema::ActOnForwardClassDeclaration(SourceLocation AtClassLoc, ClassName, TypeParams, PrevIDecl, IdentLocs[i]); IDecl->setAtEndRange(IdentLocs[i]); - + ProcessAPINotes(IDecl); + PushOnScopeChains(IDecl, TUScope); CheckObjCDeclScope(IDecl); DeclsInGroup.push_back(IDecl); diff --git a/test/APINotes/Inputs/roundtrip.apinotes b/test/APINotes/Inputs/roundtrip.apinotes index a1b60e5b19f..18bf2deeb19 100644 --- a/test/APINotes/Inputs/roundtrip.apinotes +++ b/test/APINotes/Inputs/roundtrip.apinotes @@ -7,6 +7,7 @@ Classes: Availability: available AvailabilityMsg: '' SwiftName: '' + SwiftBridge: '' Methods: - Selector: init MethodKind: Instance @@ -45,6 +46,7 @@ Classes: Availability: available AvailabilityMsg: '' SwiftName: '' + SwiftBridge: View Methods: - Selector: 'addSubview:' MethodKind: Instance From a7dced0fb9a3982b6ebb69b3d87aff538112bf10 Mon Sep 17 00:00:00 2001 From: Steven Wu Date: Thu, 10 Mar 2016 02:02:48 +0000 Subject: [PATCH 278/742] Fix false positives for for-loop-analysis warning Summary: For PseudoObjectExpr, the DeclMatcher need to search only all the semantics but also need to search pass OpaqueValueExpr for all potential uses for the Decl. Reviewers: thakis, rtrieu, rjmccall, doug.gregor Subscribers: xazax.hun, rjmccall, doug.gregor, cfe-commits Differential Revision: http://reviews.llvm.org/D17627 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263087 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/SemaStmt.cpp | 12 ++++++++++++ test/SemaObjC/warn-loop-analysis.m | 15 +++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 test/SemaObjC/warn-loop-analysis.m diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index 836073a680c..a6921b3c149 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -1416,6 +1416,18 @@ namespace { FoundDecl = true; } + void VisitPseudoObjectExpr(PseudoObjectExpr *POE) { + // Only need to visit the semantics for POE. + // SyntaticForm doesn't really use the Decal. + for (auto *S : POE->semantics()) { + if (auto *OVE = dyn_cast(S)) + // Look past the OVE into the expression it binds. + Visit(OVE->getSourceExpr()); + else + Visit(S); + } + } + bool FoundDeclInUse() { return FoundDecl; } }; // end class DeclMatcher diff --git a/test/SemaObjC/warn-loop-analysis.m b/test/SemaObjC/warn-loop-analysis.m new file mode 100644 index 00000000000..8ae7375f7f2 --- /dev/null +++ b/test/SemaObjC/warn-loop-analysis.m @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -fsyntax-only -Wloop-analysis -verify %s +// expected-no-diagnostics + +@interface MyArray +- (id)objectAtIndexedSubscript:(unsigned int)idx; +@end + +// Do not warn on objc classes has objectAtIndexedSubscript method. +MyArray *test; +void foo() +{ + unsigned int i; + for (i = 42; i > 0;) // No warnings here + (void)test[--i]; +} From 7960b46783c8dd41182ded9aad71d08f435069f6 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Fri, 11 Mar 2016 21:14:40 +0000 Subject: [PATCH 279/742] Fix ObjCMethodDecl::findPropertyDecl for class properties. This affects code completion and a few other things; hopefully the code completion test is sufficient to catch regressions. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263295 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/DeclObjC.cpp | 24 +++++++++++++++--------- test/CodeCompletion/documentation.m | 25 +++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 9 deletions(-) create mode 100644 test/CodeCompletion/documentation.m diff --git a/lib/AST/DeclObjC.cpp b/lib/AST/DeclObjC.cpp index 1480a55d56a..d2701211bea 100644 --- a/lib/AST/DeclObjC.cpp +++ b/lib/AST/DeclObjC.cpp @@ -1234,23 +1234,29 @@ ObjCMethodDecl::findPropertyDecl(bool CheckOverrides) const { if (NumArgs > 1) return nullptr; - if (!isInstanceMethod()) - return nullptr; - if (isPropertyAccessor()) { const ObjCContainerDecl *Container = cast(getParent()); bool IsGetter = (NumArgs == 0); + bool IsInstance = isInstanceMethod(); /// Local function that attempts to find a matching property within the /// given Objective-C container. auto findMatchingProperty = [&](const ObjCContainerDecl *Container) -> const ObjCPropertyDecl * { - - for (const auto *I : Container->instance_properties()) { - Selector NextSel = IsGetter ? I->getGetterName() - : I->getSetterName(); - if (NextSel == Sel) - return I; + if (IsInstance) { + for (const auto *I : Container->instance_properties()) { + Selector NextSel = IsGetter ? I->getGetterName() + : I->getSetterName(); + if (NextSel == Sel) + return I; + } + } else { + for (const auto *I : Container->class_properties()) { + Selector NextSel = IsGetter ? I->getGetterName() + : I->getSetterName(); + if (NextSel == Sel) + return I; + } } return nullptr; diff --git a/test/CodeCompletion/documentation.m b/test/CodeCompletion/documentation.m new file mode 100644 index 00000000000..47add5b6ca0 --- /dev/null +++ b/test/CodeCompletion/documentation.m @@ -0,0 +1,25 @@ +// Note: the run lines follow their respective tests, since line/column +// matter in this test. + +@interface Base +@end + +@interface Test : Base +/// Instance! +@property id instanceProp; +/// Class! +@property (class) id classProp; +@end + +void test(Test *obj) { + [obj instanceProp]; + [Test classProp]; +} + +// RUN: %clang_cc1 -fsyntax-only -code-completion-brief-comments -code-completion-at=%s:15:8 %s -o - | FileCheck -check-prefix=CHECK-CC1 %s +// CHECK-CC1: instanceProp : [#id#]instanceProp : Instance! +// CHECK-CC1: setInstanceProp: : [#void#]setInstanceProp:<#(id)#> : Instance! + +// RUN: %clang_cc1 -fsyntax-only -code-completion-brief-comments -code-completion-at=%s:16:9 %s -o - | FileCheck -check-prefix=CHECK-CC2 %s +// CHECK-CC2: classProp : [#id#]classProp : Class! +// CHECK-CC2: setClassProp: : [#void#]setClassProp:<#(id)#> : Class! From 77080f2c0365b7a0da39a9b3ba34ba2ebdb8ce78 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Fri, 11 Mar 2016 21:14:40 +0000 Subject: [PATCH 280/742] Fix ObjCMethodDecl::findPropertyDecl for class properties. This affects code completion and a few other things; hopefully the code completion test is sufficient to catch regressions. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263295 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/DeclObjC.cpp | 24 +++++++++++++++--------- test/CodeCompletion/documentation.m | 25 +++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 9 deletions(-) create mode 100644 test/CodeCompletion/documentation.m diff --git a/lib/AST/DeclObjC.cpp b/lib/AST/DeclObjC.cpp index 1480a55d56a..d2701211bea 100644 --- a/lib/AST/DeclObjC.cpp +++ b/lib/AST/DeclObjC.cpp @@ -1234,23 +1234,29 @@ ObjCMethodDecl::findPropertyDecl(bool CheckOverrides) const { if (NumArgs > 1) return nullptr; - if (!isInstanceMethod()) - return nullptr; - if (isPropertyAccessor()) { const ObjCContainerDecl *Container = cast(getParent()); bool IsGetter = (NumArgs == 0); + bool IsInstance = isInstanceMethod(); /// Local function that attempts to find a matching property within the /// given Objective-C container. auto findMatchingProperty = [&](const ObjCContainerDecl *Container) -> const ObjCPropertyDecl * { - - for (const auto *I : Container->instance_properties()) { - Selector NextSel = IsGetter ? I->getGetterName() - : I->getSetterName(); - if (NextSel == Sel) - return I; + if (IsInstance) { + for (const auto *I : Container->instance_properties()) { + Selector NextSel = IsGetter ? I->getGetterName() + : I->getSetterName(); + if (NextSel == Sel) + return I; + } + } else { + for (const auto *I : Container->class_properties()) { + Selector NextSel = IsGetter ? I->getGetterName() + : I->getSetterName(); + if (NextSel == Sel) + return I; + } } return nullptr; diff --git a/test/CodeCompletion/documentation.m b/test/CodeCompletion/documentation.m new file mode 100644 index 00000000000..47add5b6ca0 --- /dev/null +++ b/test/CodeCompletion/documentation.m @@ -0,0 +1,25 @@ +// Note: the run lines follow their respective tests, since line/column +// matter in this test. + +@interface Base +@end + +@interface Test : Base +/// Instance! +@property id instanceProp; +/// Class! +@property (class) id classProp; +@end + +void test(Test *obj) { + [obj instanceProp]; + [Test classProp]; +} + +// RUN: %clang_cc1 -fsyntax-only -code-completion-brief-comments -code-completion-at=%s:15:8 %s -o - | FileCheck -check-prefix=CHECK-CC1 %s +// CHECK-CC1: instanceProp : [#id#]instanceProp : Instance! +// CHECK-CC1: setInstanceProp: : [#void#]setInstanceProp:<#(id)#> : Instance! + +// RUN: %clang_cc1 -fsyntax-only -code-completion-brief-comments -code-completion-at=%s:16:9 %s -o - | FileCheck -check-prefix=CHECK-CC2 %s +// CHECK-CC2: classProp : [#id#]classProp : Class! +// CHECK-CC2: setClassProp: : [#void#]setClassProp:<#(id)#> : Class! From 1a5ef73ba152541b92a8ed3bbdf2eee5c1bf7b58 Mon Sep 17 00:00:00 2001 From: Anna Zaks Date: Tue, 8 Mar 2016 01:21:51 +0000 Subject: [PATCH 281/742] [analyzer] Fix missed leak from MSVC specific allocation functions Add the wide character strdup variants (wcsdup, _wcsdup) and the MSVC version of alloca (_alloca) and other differently named function used by the Malloc checker. A patch by Alexander Riccio! Differential Revision: http://reviews.llvm.org/D17688 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262894 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 3d5b3224efa4a184862806316a04db501f332eb9) --- lib/StaticAnalyzer/Checkers/MallocChecker.cpp | 36 +++++---- test/Analysis/malloc.c | 76 +++++++++++++++++++ 2 files changed, 99 insertions(+), 13 deletions(-) diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index fee030feb6d..f365e536676 100644 --- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -169,11 +169,12 @@ class MallocChecker : public Checker BT_MismatchedDealloc; mutable std::unique_ptr BT_OffsetFree[CK_NumCheckKinds]; mutable std::unique_ptr BT_UseZerroAllocated[CK_NumCheckKinds]; - mutable IdentifierInfo *II_alloca, *II_malloc, *II_free, *II_realloc, - *II_calloc, *II_valloc, *II_reallocf, *II_strndup, - *II_strdup, *II_kmalloc, *II_if_nameindex, - *II_if_freenameindex; + mutable IdentifierInfo *II_alloca, *II_win_alloca, *II_malloc, *II_free, + *II_realloc, *II_calloc, *II_valloc, *II_reallocf, + *II_strndup, *II_strdup, *II_win_strdup, *II_kmalloc, + *II_if_nameindex, *II_if_freenameindex, *II_wcsdup, + *II_win_wcsdup; mutable Optional KernelZeroFlagVal; void initIdentifierInfo(ASTContext &C) const; @@ -540,9 +542,15 @@ void MallocChecker::initIdentifierInfo(ASTContext &Ctx) const { II_valloc = &Ctx.Idents.get("valloc"); II_strdup = &Ctx.Idents.get("strdup"); II_strndup = &Ctx.Idents.get("strndup"); + II_wcsdup = &Ctx.Idents.get("wcsdup"); II_kmalloc = &Ctx.Idents.get("kmalloc"); II_if_nameindex = &Ctx.Idents.get("if_nameindex"); II_if_freenameindex = &Ctx.Idents.get("if_freenameindex"); + + //MSVC uses `_`-prefixed instead, so we check for them too. + II_win_strdup = &Ctx.Idents.get("_strdup"); + II_win_wcsdup = &Ctx.Idents.get("_wcsdup"); + II_win_alloca = &Ctx.Idents.get("_alloca"); } bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const { @@ -585,7 +593,8 @@ bool MallocChecker::isCMemFunction(const FunctionDecl *FD, if (Family == AF_Malloc && CheckAlloc) { if (FunI == II_malloc || FunI == II_realloc || FunI == II_reallocf || FunI == II_calloc || FunI == II_valloc || FunI == II_strdup || - FunI == II_strndup || FunI == II_kmalloc) + FunI == II_win_strdup || FunI == II_strndup || FunI == II_wcsdup || + FunI == II_win_wcsdup || FunI == II_kmalloc) return true; } @@ -600,7 +609,7 @@ bool MallocChecker::isCMemFunction(const FunctionDecl *FD, } if (Family == AF_Alloca && CheckAlloc) { - if (FunI == II_alloca) + if (FunI == II_alloca || FunI == II_win_alloca) return true; } } @@ -789,11 +798,12 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { State = ProcessZeroAllocation(C, CE, 1, State); } else if (FunI == II_free) { State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory); - } else if (FunI == II_strdup) { + } else if (FunI == II_strdup || FunI == II_win_strdup || + FunI == II_wcsdup || FunI == II_win_wcsdup) { State = MallocUpdateRefState(C, CE, State); } else if (FunI == II_strndup) { State = MallocUpdateRefState(C, CE, State); - } else if (FunI == II_alloca) { + } else if (FunI == II_alloca || FunI == II_win_alloca) { State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, AF_Alloca); State = ProcessZeroAllocation(C, CE, 0, State); diff --git a/test/Analysis/malloc.c b/test/Analysis/malloc.c index 881eb38ad84..30d72691956 100644 --- a/test/Analysis/malloc.c +++ b/test/Analysis/malloc.c @@ -4,6 +4,21 @@ void clang_analyzer_eval(int); +// Without -fms-compatibility, wchar_t isn't a builtin type. MSVC defines +// _WCHAR_T_DEFINED if wchar_t is available. Microsoft recommends that you use +// the builtin type: "Using the typedef version can cause portability +// problems", but we're ok here because we're not actually running anything. +// Also of note is this cryptic warning: "The wchar_t type is not supported +// when you compile C code". +// +// See the docs for more: +// https://msdn.microsoft.com/en-us/library/dh8che7s.aspx +#if !defined(_WCHAR_T_DEFINED) +// "Microsoft implements wchar_t as a two-byte unsigned value" +typedef unsigned short wchar_t; +#define _WCHAR_T_DEFINED +#endif // !defined(_WCHAR_T_DEFINED) + typedef __typeof(sizeof(int)) size_t; void *malloc(size_t); void *alloca(size_t); @@ -13,9 +28,15 @@ void *realloc(void *ptr, size_t size); void *reallocf(void *ptr, size_t size); void *calloc(size_t nmemb, size_t size); char *strdup(const char *s); +wchar_t *wcsdup(const wchar_t *s); char *strndup(const char *s, size_t n); int memcmp(const void *s1, const void *s2, size_t n); +// Windows variants +char *_strdup(const char *strSource); +wchar_t *_wcsdup(const wchar_t *strSource); +void *_alloca(size_t size); + void myfoo(int *p); void myfooint(int p); char *fooRetPtr(); @@ -55,6 +76,10 @@ void allocaTest() { int *p = alloca(sizeof(int)); } // no warn +void winAllocaTest() { + int *p = _alloca(sizeof(int)); +} // no warn + void allocaBuiltinTest() { int *p = __builtin_alloca(sizeof(int)); } // no warn @@ -210,6 +235,11 @@ void CheckUseZeroAllocatedNoWarn2() { int *p = alloca(0); // no warning } +void CheckUseZeroWinAllocatedNoWarn2() { + int *p = _alloca(0); // no warning +} + + void CheckUseZeroAllocatedNoWarn3() { int *p = malloc(0); int *q = realloc(p, 8); // no warning @@ -233,6 +263,11 @@ char CheckUseZeroAllocated2() { return *p; // expected-warning {{Use of zero-allocated memory}} } +char CheckUseZeroWinAllocated2() { + char *p = _alloca(0); + return *p; // expected-warning {{Use of zero-allocated memory}} +} + void UseZeroAllocated(int *p) { if (p) *p = 7; // expected-warning {{Use of zero-allocated memory}} @@ -1076,6 +1111,21 @@ void testStrdup(const char *s, unsigned validIndex) { s2[validIndex + 1] = 'b'; } // expected-warning {{Potential leak of memory pointed to by}} +void testWinStrdup(const char *s, unsigned validIndex) { + char *s2 = _strdup(s); + s2[validIndex + 1] = 'b'; +} // expected-warning {{Potential leak of memory pointed to by}} + +void testWcsdup(const wchar_t *s, unsigned validIndex) { + wchar_t *s2 = wcsdup(s); + s2[validIndex + 1] = 'b'; +} // expected-warning {{Potential leak of memory pointed to by}} + +void testWinWcsdup(const wchar_t *s, unsigned validIndex) { + wchar_t *s2 = _wcsdup(s); + s2[validIndex + 1] = 'b'; +} // expected-warning {{Potential leak of memory pointed to by}} + int testStrndup(const char *s, unsigned validIndex, unsigned size) { char *s2 = strndup(s, size); s2 [validIndex + 1] = 'b'; @@ -1091,6 +1141,24 @@ void testStrdupContentIsDefined(const char *s, unsigned validIndex) { free(s2); } +void testWinStrdupContentIsDefined(const char *s, unsigned validIndex) { + char *s2 = _strdup(s); + char result = s2[1];// no warning + free(s2); +} + +void testWcsdupContentIsDefined(const wchar_t *s, unsigned validIndex) { + wchar_t *s2 = wcsdup(s); + wchar_t result = s2[1];// no warning + free(s2); +} + +void testWinWcsdupContentIsDefined(const wchar_t *s, unsigned validIndex) { + wchar_t *s2 = _wcsdup(s); + wchar_t result = s2[1];// no warning + free(s2); +} + // ---------------------------------------------------------------------------- // Test the system library functions to which the pointer can escape. // This tests false positive suppression. @@ -1444,6 +1512,14 @@ char *testLeakWithinReturn(char *str) { return strdup(strdup(str)); // expected-warning{{leak}} } +char *testWinLeakWithinReturn(char *str) { + return _strdup(_strdup(str)); // expected-warning{{leak}} +} + +wchar_t *testWinWideLeakWithinReturn(wchar_t *str) { + return _wcsdup(_wcsdup(str)); // expected-warning{{leak}} +} + void passConstPtr(const char * ptr); void testPassConstPointer() { From 0d78db4ded4ffe4dc16d85371b6b5201461b6971 Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Thu, 10 Mar 2016 18:53:19 +0000 Subject: [PATCH 282/742] Add TreatUnavailableAsInvalid for the verification-only mode in InitListChecker. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Given the following test case: typedef struct { const char *name; id field; } Test9; extern void doSomething(Test9 arg); void test9() { Test9 foo2 = {0, 0}; doSomething(foo2); } With a release compiler, we don't emit any message and silently ignore the variable "foo2". With an assert compiler, we get an assertion failure. The root cause ————————————— Back in r140457 we gave InitListChecker a verification-only mode, and will use CanUseDecl instead of DiagnoseUseOfDecl for verification-only mode. These two functions handle unavailable issues differently: In Sema::CanUseDecl, we say the decl is invalid when the Decl is unavailable and the current context is available. In Sema::DiagnoseUseOfDecl, we say the decl is usable by ignoring the return code of DiagnoseAvailabilityOfDecl So with an assert build, we will hit an assertion in diagnoseListInit assert(DiagnoseInitList.HadError() && "Inconsistent init list check result."); The fix ------------------- If we follow what is implemented in CanUseDecl and treat Decls with unavailable issues as invalid, the variable decl of “foo2” will be marked as invalid. Since unavailable checking is processed in delayed diagnostics (r197627), we will silently ignore the diagnostics when we find out that the variable decl is invalid. We add a flag "TreatUnavailableAsInvalid" for the verification-only mode. For overload resolution, we want to say decls with unavailable issues are invalid; but for everything else, we should say they are valid and emit diagnostics. Depending on the value of the flag, CanUseDecl can return different values for unavailable issues. rdar://23557300 Differential Revision: http://reviews.llvm.org/D15314 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263149 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Sema/Initialization.h | 7 +- include/clang/Sema/Sema.h | 2 +- lib/Sema/SemaDecl.cpp | 4 +- lib/Sema/SemaExpr.cpp | 4 +- lib/Sema/SemaInit.cpp | 84 ++++++++++++++++-------- test/SemaObjC/Inputs/arc-system-header.h | 5 ++ test/SemaObjC/arc-system-header.m | 7 ++ 7 files changed, 78 insertions(+), 35 deletions(-) diff --git a/include/clang/Sema/Initialization.h b/include/clang/Sema/Initialization.h index d4f57b70112..1022cc0d3e5 100644 --- a/include/clang/Sema/Initialization.h +++ b/include/clang/Sema/Initialization.h @@ -885,14 +885,17 @@ class InitializationSequence { /// \param TopLevelOfInitList true if we are initializing from an expression /// at the top level inside an initializer list. This disallows /// narrowing conversions in C++11 onwards. + /// \param TreatUnavailableAsInvalid true if we want to treat unavailable + /// as invalid. InitializationSequence(Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind, MultiExprArg Args, - bool TopLevelOfInitList = false); + bool TopLevelOfInitList = false, + bool TreatUnavailableAsInvalid = true); void InitializeFrom(Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind, MultiExprArg Args, - bool TopLevelOfInitList); + bool TopLevelOfInitList, bool TreatUnavailableAsInvalid); ~InitializationSequence(); diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 1ccd027ca71..e7a3cc3ef49 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -3563,7 +3563,7 @@ class Sema { //===--------------------------------------------------------------------===// // Expression Parsing Callbacks: SemaExpr.cpp. - bool CanUseDecl(NamedDecl *D); + bool CanUseDecl(NamedDecl *D, bool TreatUnavailableAsInvalid); bool DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc, const ObjCInterfaceDecl *UnknownObjCClass=nullptr, bool ObjCPropertyAccess=false); diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 6dd210fb497..f48ce082fa2 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -9406,7 +9406,9 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, if (VDecl->isInvalidDecl()) return; - InitializationSequence InitSeq(*this, Entity, Kind, Args); + InitializationSequence InitSeq(*this, Entity, Kind, Args, + /*TopLevelOfInitList=*/false, + /*TreatUnavailableAsInvalid=*/false); ExprResult Result = InitSeq.Perform(*this, Entity, Kind, Args, &DclT); if (Result.isInvalid()) { VDecl->setInvalidDecl(); diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index a9fe9a0100b..715449204fa 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -49,7 +49,7 @@ using namespace sema; /// \brief Determine whether the use of this declaration is valid, without /// emitting diagnostics. -bool Sema::CanUseDecl(NamedDecl *D) { +bool Sema::CanUseDecl(NamedDecl *D, bool TreatUnavailableAsInvalid) { // See if this is an auto-typed variable whose initializer we are parsing. if (ParsingInitForAutoVars.count(D)) return false; @@ -67,7 +67,7 @@ bool Sema::CanUseDecl(NamedDecl *D) { } // See if this function is unavailable. - if (D->getAvailability() == AR_Unavailable && + if (TreatUnavailableAsInvalid && D->getAvailability() == AR_Unavailable && cast(CurContext)->getAvailability() != AR_Unavailable) return false; diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index df92e930682..c0c57f35f68 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -236,6 +236,7 @@ class InitListChecker { Sema &SemaRef; bool hadError; bool VerifyOnly; // no diagnostics, no structure building + bool TreatUnavailableAsInvalid; // Used only in VerifyOnly mode. llvm::DenseMap SyntacticToSemantic; InitListExpr *FullyStructuredList; @@ -317,7 +318,8 @@ class InitListChecker { static ExprResult PerformEmptyInit(Sema &SemaRef, SourceLocation Loc, const InitializedEntity &Entity, - bool VerifyOnly); + bool VerifyOnly, + bool TreatUnavailableAsInvalid); // Explanation on the "FillWithNoInit" mode: // @@ -353,7 +355,8 @@ class InitListChecker { public: InitListChecker(Sema &S, const InitializedEntity &Entity, - InitListExpr *IL, QualType &T, bool VerifyOnly); + InitListExpr *IL, QualType &T, bool VerifyOnly, + bool TreatUnavailableAsInvalid); bool HadError() { return hadError; } // @brief Retrieves the fully-structured initializer list used for @@ -365,7 +368,8 @@ class InitListChecker { ExprResult InitListChecker::PerformEmptyInit(Sema &SemaRef, SourceLocation Loc, const InitializedEntity &Entity, - bool VerifyOnly) { + bool VerifyOnly, + bool TreatUnavailableAsInvalid) { InitializationKind Kind = InitializationKind::CreateValue(Loc, Loc, Loc, true); MultiExprArg SubInit; @@ -437,7 +441,8 @@ ExprResult InitListChecker::PerformEmptyInit(Sema &SemaRef, InitSeq.InitializeFrom( SemaRef, Entity, InitializationKind::CreateValue(Loc, Loc, Loc, true), - MultiExprArg(), /*TopLevelOfInitList=*/false); + MultiExprArg(), /*TopLevelOfInitList=*/false, + TreatUnavailableAsInvalid); // Emit a warning for this. System header warnings aren't shown // by default, but people working on system headers should see it. if (!VerifyOnly) { @@ -474,7 +479,8 @@ void InitListChecker::CheckEmptyInitializable(const InitializedEntity &Entity, SourceLocation Loc) { assert(VerifyOnly && "CheckEmptyInitializable is only inteded for verification mode."); - if (PerformEmptyInit(SemaRef, Loc, Entity, /*VerifyOnly*/true).isInvalid()) + if (PerformEmptyInit(SemaRef, Loc, Entity, /*VerifyOnly*/true, + TreatUnavailableAsInvalid).isInvalid()) hadError = true; } @@ -535,7 +541,8 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field, } ExprResult MemberInit = PerformEmptyInit(SemaRef, Loc, MemberEntity, - /*VerifyOnly*/false); + /*VerifyOnly*/false, + TreatUnavailableAsInvalid); if (MemberInit.isInvalid()) { hadError = true; return; @@ -661,7 +668,8 @@ InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity, else { ExprResult ElementInit = PerformEmptyInit(SemaRef, ILE->getLocEnd(), ElementEntity, - /*VerifyOnly*/false); + /*VerifyOnly*/false, + TreatUnavailableAsInvalid); if (ElementInit.isInvalid()) { hadError = true; return; @@ -710,8 +718,10 @@ InitListChecker::FillInEmptyInitializations(const InitializedEntity &Entity, InitListChecker::InitListChecker(Sema &S, const InitializedEntity &Entity, InitListExpr *IL, QualType &T, - bool VerifyOnly) - : SemaRef(S), VerifyOnly(VerifyOnly) { + bool VerifyOnly, + bool TreatUnavailableAsInvalid) + : SemaRef(S), VerifyOnly(VerifyOnly), + TreatUnavailableAsInvalid(TreatUnavailableAsInvalid) { // FIXME: Check that IL isn't already the semantic form of some other // InitListExpr. If it is, we'd create a broken AST. @@ -1782,7 +1792,7 @@ void InitListChecker::CheckStructUnionTypes(const InitializedEntity &Entity, // Make sure we can use this declaration. bool InvalidUse; if (VerifyOnly) - InvalidUse = !SemaRef.CanUseDecl(*Field); + InvalidUse = !SemaRef.CanUseDecl(*Field, TreatUnavailableAsInvalid); else InvalidUse = SemaRef.DiagnoseUseOfDecl(*Field, IList->getInit(Index)->getLocStart()); @@ -2186,7 +2196,7 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity, // Make sure we can use this declaration. bool InvalidUse; if (VerifyOnly) - InvalidUse = !SemaRef.CanUseDecl(*Field); + InvalidUse = !SemaRef.CanUseDecl(*Field, TreatUnavailableAsInvalid); else InvalidUse = SemaRef.DiagnoseUseOfDecl(*Field, D->getFieldLoc()); if (InvalidUse) { @@ -3313,7 +3323,8 @@ static void TryListInitialization(Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind, InitListExpr *InitList, - InitializationSequence &Sequence); + InitializationSequence &Sequence, + bool TreatUnavailableAsInvalid); /// \brief When initializing from init list via constructor, handle /// initialization of an object of type std::initializer_list. @@ -3323,7 +3334,8 @@ static void TryListInitialization(Sema &S, static bool TryInitializerListConstruction(Sema &S, InitListExpr *List, QualType DestType, - InitializationSequence &Sequence) { + InitializationSequence &Sequence, + bool TreatUnavailableAsInvalid) { QualType E; if (!S.isStdInitializerList(DestType, &E)) return false; @@ -3342,7 +3354,8 @@ static bool TryInitializerListConstruction(Sema &S, InitializedEntity::InitializeTemporary(ArrayType); InitializationKind Kind = InitializationKind::CreateDirectList(List->getExprLoc()); - TryListInitialization(S, HiddenArray, Kind, List, Sequence); + TryListInitialization(S, HiddenArray, Kind, List, Sequence, + TreatUnavailableAsInvalid); if (Sequence) Sequence.AddStdInitializerListConstructionStep(DestType); return true; @@ -3591,7 +3604,8 @@ static void TryReferenceListInitialization(Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind, InitListExpr *InitList, - InitializationSequence &Sequence) { + InitializationSequence &Sequence, + bool TreatUnavailableAsInvalid) { // First, catch C++03 where this isn't possible. if (!S.getLangOpts().CPlusPlus11) { Sequence.SetFailed(InitializationSequence::FK_ReferenceBindingToInitList); @@ -3647,7 +3661,8 @@ static void TryReferenceListInitialization(Sema &S, // Not reference-related. Create a temporary and bind to that. InitializedEntity TempEntity = InitializedEntity::InitializeTemporary(cv1T1); - TryListInitialization(S, TempEntity, Kind, InitList, Sequence); + TryListInitialization(S, TempEntity, Kind, InitList, Sequence, + TreatUnavailableAsInvalid); if (Sequence) { if (DestType->isRValueReferenceType() || (T1Quals.hasConst() && !T1Quals.hasVolatile())) @@ -3663,7 +3678,8 @@ static void TryListInitialization(Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind, InitListExpr *InitList, - InitializationSequence &Sequence) { + InitializationSequence &Sequence, + bool TreatUnavailableAsInvalid) { QualType DestType = Entity.getType(); // C++ doesn't allow scalar initialization with more than one argument. @@ -3674,7 +3690,8 @@ static void TryListInitialization(Sema &S, return; } if (DestType->isReferenceType()) { - TryReferenceListInitialization(S, Entity, Kind, InitList, Sequence); + TryReferenceListInitialization(S, Entity, Kind, InitList, Sequence, + TreatUnavailableAsInvalid); return; } @@ -3718,7 +3735,8 @@ static void TryListInitialization(Sema &S, InitList->getRBraceLoc()) : Kind; Sequence.InitializeFrom(S, Entity, SubKind, SubInit, - /*TopLevelOfInitList*/ true); + /*TopLevelOfInitList*/ true, + TreatUnavailableAsInvalid); // TryStringLiteralInitialization() (in InitializeFrom()) will fail if // the element is not an appropriately-typed string literal, in which @@ -3750,7 +3768,8 @@ static void TryListInitialization(Sema &S, // - Otherwise, if T is a specialization of std::initializer_list, // an initializer_list object constructed [...] - if (TryInitializerListConstruction(S, InitList, DestType, Sequence)) + if (TryInitializerListConstruction(S, InitList, DestType, Sequence, + TreatUnavailableAsInvalid)) return; // - Otherwise, if T is a class type, constructors are considered. @@ -3786,14 +3805,15 @@ static void TryListInitialization(Sema &S, : Kind; Expr *SubInit[1] = { InitList->getInit(0) }; Sequence.InitializeFrom(S, Entity, SubKind, SubInit, - /*TopLevelOfInitList*/true); + /*TopLevelOfInitList*/true, + TreatUnavailableAsInvalid); if (Sequence) Sequence.RewrapReferenceInitList(Entity.getType(), InitList); return; } InitListChecker CheckInitList(S, Entity, InitList, - DestType, /*VerifyOnly=*/true); + DestType, /*VerifyOnly=*/true, TreatUnavailableAsInvalid); if (CheckInitList.HadError()) { Sequence.SetFailed(InitializationSequence::FK_ListInitializationFailed); return; @@ -4800,9 +4820,11 @@ InitializationSequence::InitializationSequence(Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind, MultiExprArg Args, - bool TopLevelOfInitList) + bool TopLevelOfInitList, + bool TreatUnavailableAsInvalid) : FailedCandidateSet(Kind.getLocation(), OverloadCandidateSet::CSK_Normal) { - InitializeFrom(S, Entity, Kind, Args, TopLevelOfInitList); + InitializeFrom(S, Entity, Kind, Args, TopLevelOfInitList, + TreatUnavailableAsInvalid); } /// Tries to get a FunctionDecl out of `E`. If it succeeds and we can take the @@ -4820,7 +4842,8 @@ void InitializationSequence::InitializeFrom(Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind, MultiExprArg Args, - bool TopLevelOfInitList) { + bool TopLevelOfInitList, + bool TreatUnavailableAsInvalid) { ASTContext &Context = S.Context; // Eliminate non-overload placeholder types in the arguments. We @@ -4874,7 +4897,8 @@ void InitializationSequence::InitializeFrom(Sema &S, // object is list-initialized (8.5.4). if (Kind.getKind() != InitializationKind::IK_Direct) { if (InitListExpr *InitList = dyn_cast_or_null(Initializer)) { - TryListInitialization(S, Entity, Kind, InitList, *this); + TryListInitialization(S, Entity, Kind, InitList, *this, + TreatUnavailableAsInvalid); return; } } @@ -4958,7 +4982,7 @@ void InitializationSequence::InitializeFrom(Sema &S, Entity.getKind() == InitializedEntity::EK_Member && Initializer && isa(Initializer)) { TryListInitialization(S, Entity, Kind, cast(Initializer), - *this); + *this, TreatUnavailableAsInvalid); AddParenthesizedArrayInitStep(DestType); } else if (DestAT->getElementType()->isCharType()) SetFailed(FK_ArrayNeedsInitListOrStringLiteral); @@ -6499,7 +6523,8 @@ InitializationSequence::Perform(Sema &S, InitializedEntity TempEntity = InitializedEntity::InitializeTemporary(Ty); InitializedEntity InitEntity = IsTemporary ? TempEntity : Entity; InitListChecker PerformInitList(S, InitEntity, - InitList, Ty, /*VerifyOnly=*/false); + InitList, Ty, /*VerifyOnly=*/false, + /*TreatUnavailableAsInvalid=*/false); if (PerformInitList.HadError()) return ExprError(); @@ -6870,7 +6895,8 @@ static void diagnoseListInit(Sema &S, const InitializedEntity &Entity, } InitListChecker DiagnoseInitList(S, Entity, InitList, DestType, - /*VerifyOnly=*/false); + /*VerifyOnly=*/false, + /*TreatUnavailableAsInvalid=*/false); assert(DiagnoseInitList.HadError() && "Inconsistent init list check result."); } diff --git a/test/SemaObjC/Inputs/arc-system-header.h b/test/SemaObjC/Inputs/arc-system-header.h index 5012a2a3783..9decc5efce6 100644 --- a/test/SemaObjC/Inputs/arc-system-header.h +++ b/test/SemaObjC/Inputs/arc-system-header.h @@ -50,3 +50,8 @@ extern struct Test6 *const kMagicConstant; static inline void *test8(id ptr) { return (__bridge_retain void*) ptr; } + +typedef struct { + const char *name; + id field; +} Test9; diff --git a/test/SemaObjC/arc-system-header.m b/test/SemaObjC/arc-system-header.m index acfd9a8585e..68230e74b2d 100644 --- a/test/SemaObjC/arc-system-header.m +++ b/test/SemaObjC/arc-system-header.m @@ -46,6 +46,13 @@ void test7(Test7 *p) { // expected-note@arc-system-header.h:41 4 {{declaration uses type that is ill-formed in ARC}} // expected-note@arc-system-header.h:41 2 {{property 'prop' is declared unavailable here}} } + +extern void doSomething(Test9 arg); +void test9() { + Test9 foo2 = {0, 0}; // expected-error {{'field' is unavailable in ARC}} + // expected-note@arc-system-header.h:56 {{field has non-trivial ownership qualification}} + doSomething(foo2); +} #endif // test8 in header From 9fbea6a419a8844c257676acc153f0e264a7d063 Mon Sep 17 00:00:00 2001 From: Chris Bieneman Date: Mon, 14 Mar 2016 20:23:21 +0000 Subject: [PATCH 283/742] [CMake] Updating Apple Clang CMake caches This is a big update that gets the public configurations more in line with the ones we're actually using internally to ship Clang in Xcode. From here forward I expect most of the changes in these files to be incremental as the changes get made internally. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263483 91177308-0d34-0410-b5e6-96231b3b80d8 --- cmake/caches/Apple-stage1.cmake | 22 +++++++++++++++- cmake/caches/Apple-stage2.cmake | 45 +++++++++++++++++++++++++++------ 2 files changed, 58 insertions(+), 9 deletions(-) diff --git a/cmake/caches/Apple-stage1.cmake b/cmake/caches/Apple-stage1.cmake index a5c3fdbbdfd..a98b2ab8a1f 100644 --- a/cmake/caches/Apple-stage1.cmake +++ b/cmake/caches/Apple-stage1.cmake @@ -16,15 +16,35 @@ set(LLVM_INCLUDE_DOCS OFF CACHE BOOL "") set(CLANG_INCLUDE_TESTS OFF CACHE BOOL "") set(COMPILER_RT_INCLUDE_TESTS OFF CACHE BOOL "") set(COMPILER_RT_BUILD_SANITIZERS OFF CACHE BOOL "") +set(CMAKE_MACOSX_RPATH ON CACHE BOOL "") +set(LLVM_ENABLE_ZLIB OFF CACHE BOOL "") +set(LLVM_ENABLE_BACKTRACES OFF CACHE BOOL "") +set(CLANG_PLUGIN_SUPPORT OFF CACHE BOOL "") +set(CLANG_BOOTSTRAP_PASSTHROUGH + CMAKE_OSX_ARCHITECTURES + CACHE STRING "") +set(BOOTSTRAP_LLVM_ENABLE_LTO ON CACHE BOOL "") set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "") -set(PACKAGE_VERSION 7.1.0 CACHE STRING "") # LIBCXX Settings set(LIBCXX_INSTALL_LIBRARY OFF CACHE BOOL "") set(LIBCXX_INSTALL_HEADERS ON CACHE BOOL "") set(LIBCXX_OVERRIDE_DARWIN_INSTALL ON CACHE BOOL "") +set(CLANG_BOOTSTRAP_TARGETS + generate-order-file + check-all + check-llvm + check-clang + test-suite + test-depends + llvm-test-depends + clang-test-depends + distribution + install-distribution + clang CACHE STRING "") + #bootstrap set(CLANG_ENABLE_BOOTSTRAP ON CACHE BOOL "") set(CLANG_BOOTSTRAP_CMAKE_ARGS diff --git a/cmake/caches/Apple-stage2.cmake b/cmake/caches/Apple-stage2.cmake index bb319aaeb86..b10fb935895 100644 --- a/cmake/caches/Apple-stage2.cmake +++ b/cmake/caches/Apple-stage2.cmake @@ -3,22 +3,37 @@ set(LLVM_TARGETS_TO_BUILD X86 ARM AArch64 CACHE STRING "") set(CLANG_VENDOR Apple CACHE STRING "") -set(LLVM_INCLUDE_TESTS OFF CACHE BOOL "") set(LLVM_INCLUDE_EXAMPLES OFF CACHE BOOL "") -set(LLVM_INCLUDE_UTILS OFF CACHE BOOL "") set(LLVM_INCLUDE_DOCS OFF CACHE BOOL "") -set(CLANG_INCLUDE_TESTS OFF CACHE BOOL "") -set(COMPILER_RT_INCLUDE_TESTS OFF CACHE BOOL "") -set(COMPILER_RT_BUILD_SANITIZERS OFF CACHE BOOL "") +set(LLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD OFF CACHE BOOL "") +set(CLANG_TOOL_SCAN_BUILD_BUILD OFF CACHE BOOL "") +set(CLANG_TOOL_SCAN_VIEW_BUILD OFF CACHE BOOL "") set(CLANG_LINKS_TO_CREATE clang++ cc c++ CACHE STRING "") +set(CMAKE_MACOSX_RPATH ON CACHE BOOL "") +set(LLVM_ENABLE_ZLIB OFF CACHE BOOL "") +set(LLVM_ENABLE_BACKTRACES OFF CACHE BOOL "") +set(LLVM_EXTERNALIZE_DEBUGINFO ON CACHE BOOL "") +set(CLANG_PLUGIN_SUPPORT OFF CACHE BOOL "") +set(BUG_REPORT_URL "http://developer.apple.com/bugreporter/" CACHE STRING "") +set(LLVM_ENABLE_TIMESTAMPS OFF CACHE BOOL "Don't time-stamp shipping builds - this makes builds reproducible") + +# Make unit tests (if present) part of the ALL target +set(LLVM_BUILD_TESTS ON CACHE BOOL "") +# Don't build or run the compiler-rt tests +set(COMPILER_RT_INCLUDE_TESTS OFF CACHE BOOL "") -set(CMAKE_C_FLAGS_RELWITHDEBINFO "-Os -flto -gline-tables-only -DNDEBUG" CACHE STRING "") -set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-Os -flto -gline-tables-only -DNDEBUG" CACHE STRING "") +set(LLVM_ENABLE_LTO ON CACHE BOOL "") +set(CMAKE_C_FLAGS "-fno-stack-protector -fno-common -Wno-profile-instr-unprofiled" CACHE STRING "") +set(CMAKE_CXX_FLAGS "-fno-stack-protector -fno-common -Wno-profile-instr-unprofiled" CACHE STRING "") +set(CMAKE_EXE_LINKER_FLAGS "-fno-pie" CACHE STRING "") +set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -gline-tables-only -DNDEBUG" CACHE STRING "") +set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -gline-tables-only -DNDEBUG" CACHE STRING "") set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "") -set(PACKAGE_VERSION 7.1.0 CACHE STRING "") set(LIBCXX_INSTALL_LIBRARY OFF CACHE BOOL "") set(LIBCXX_INSTALL_HEADERS OFF CACHE BOOL "") +set(LIBCXX_INCLUDE_TESTS OFF CACHE BOOL "") +set(LLVM_LTO_VERSION_OFFSET 3000 CACHE STRING "") # setup toolchain set(LLVM_INSTALL_TOOLCHAIN_ONLY ON CACHE BOOL "") @@ -27,4 +42,18 @@ set(LLVM_TOOLCHAIN_TOOLS llvm-cov llvm-dwarfdump llvm-profdata + llvm-objdump + llvm-nm + llvm-size CACHE STRING "") + +set(LLVM_DISTRIBUTION_COMPONENTS + clang + LTO + clang-format + ${LLVM_TOOLCHAIN_TOOLS} + CACHE STRING "") + +# test args + +set(LLVM_LIT_ARGS "--xunit-xml-output=testresults.xunit.xml -v" CACHE STRING "") From 7d62aba0d5f3b5b66878e0a7ed3190e349b8e85a Mon Sep 17 00:00:00 2001 From: Chris Bieneman Date: Tue, 15 Mar 2016 18:25:36 +0000 Subject: [PATCH 284/742] [CMake] Defaulting CLANG_VENDOR to PACKAGE_VENDOR LLVM r263566 adds a generic PACKAGE_VENDOR configuration which can be used to specify the vendor for LLVM toolchain tools. This change defaults the CLANG_VENDOR to the PACKAGE_VENDOR so that you don't have to specify both when building a package. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263570 91177308-0d34-0410-b5e6-96231b3b80d8 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8bf9e26e3cc..c6a90e4ac80 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -200,7 +200,7 @@ set(DEFAULT_SYSROOT "" CACHE PATH set(CLANG_DEFAULT_OPENMP_RUNTIME "libomp" CACHE STRING "Default OpenMP runtime used by -fopenmp.") -set(CLANG_VENDOR "" CACHE STRING +set(CLANG_VENDOR ${PACKAGE_VENDOR} CACHE STRING "Vendor-specific text for showing with version information.") if( CLANG_VENDOR ) From 63e1858a047adec3715008b87f051f0b9ddd670e Mon Sep 17 00:00:00 2001 From: Chris Bieneman Date: Tue, 15 Mar 2016 18:27:28 +0000 Subject: [PATCH 285/742] [CMake] Updating Apple build configurations This updates Apple build configurations to adapt to r263566 & r263570, which added a PACKAGE_VENDOR variable. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263571 91177308-0d34-0410-b5e6-96231b3b80d8 --- cmake/caches/Apple-stage2.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/caches/Apple-stage2.cmake b/cmake/caches/Apple-stage2.cmake index b10fb935895..55481db6cf0 100644 --- a/cmake/caches/Apple-stage2.cmake +++ b/cmake/caches/Apple-stage2.cmake @@ -2,7 +2,7 @@ # specified by the stage1 build. set(LLVM_TARGETS_TO_BUILD X86 ARM AArch64 CACHE STRING "") -set(CLANG_VENDOR Apple CACHE STRING "") +set(PACKAGE_VENDOR Apple CACHE STRING "") set(LLVM_INCLUDE_EXAMPLES OFF CACHE BOOL "") set(LLVM_INCLUDE_DOCS OFF CACHE BOOL "") set(LLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD OFF CACHE BOOL "") From 6c143ec0c3ea9134552efd4d99ce31f775d76f92 Mon Sep 17 00:00:00 2001 From: Bob Wilson Date: Fri, 11 Mar 2016 21:55:37 +0000 Subject: [PATCH 286/742] Add fix-it for format-security warnings. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263299 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/SemaChecking.cpp | 30 +++++++++++++++------- test/Sema/format-strings-fixit.c | 6 +++++ test/SemaObjC/format-strings-objc-fixit.m | 31 +++++++++++++++++++++++ 3 files changed, 58 insertions(+), 9 deletions(-) create mode 100644 test/SemaObjC/format-strings-objc-fixit.m diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index 4c3dd487f19..982772923ca 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -3278,20 +3278,32 @@ bool Sema::CheckFormatArguments(ArrayRef Args, // format is either NSString or CFString. This is a hack to prevent // diag when using the NSLocalizedString and CFCopyLocalizedString macros // which are usually used in place of NS and CF string literals. - if (Type == FST_NSString && - SourceMgr.isInSystemMacro(Args[format_idx]->getLocStart())) + SourceLocation FormatLoc = Args[format_idx]->getLocStart(); + if (Type == FST_NSString && SourceMgr.isInSystemMacro(FormatLoc)) return false; // If there are no arguments specified, warn with -Wformat-security, otherwise // warn only with -Wformat-nonliteral. - if (Args.size() == firstDataArg) - Diag(Args[format_idx]->getLocStart(), - diag::warn_format_nonliteral_noargs) + if (Args.size() == firstDataArg) { + const SemaDiagnosticBuilder &D = + Diag(FormatLoc, diag::warn_format_nonliteral_noargs); + switch (Type) { + default: + D << OrigFormatExpr->getSourceRange(); + break; + case FST_Kprintf: + case FST_FreeBSDKPrintf: + case FST_Printf: + D << FixItHint::CreateInsertion(FormatLoc, "\"%s\", "); + break; + case FST_NSString: + D << FixItHint::CreateInsertion(FormatLoc, "@\"%@\", "); + break; + } + } else { + Diag(FormatLoc, diag::warn_format_nonliteral) << OrigFormatExpr->getSourceRange(); - else - Diag(Args[format_idx]->getLocStart(), - diag::warn_format_nonliteral) - << OrigFormatExpr->getSourceRange(); + } return false; } diff --git a/test/Sema/format-strings-fixit.c b/test/Sema/format-strings-fixit.c index b982eb45e5f..fb617dd500f 100644 --- a/test/Sema/format-strings-fixit.c +++ b/test/Sema/format-strings-fixit.c @@ -16,6 +16,8 @@ typedef __UINTMAX_TYPE__ uintmax_t; typedef __PTRDIFF_TYPE__ ptrdiff_t; typedef __WCHAR_TYPE__ wchar_t; +extern const char *NonliteralString; + void test() { // Basic types printf("%s", (int) 123); @@ -94,6 +96,9 @@ void test() { printf("%G", (long double) 42); printf("%a", (long double) 42); printf("%A", (long double) 42); + + // nonliteral format + printf(NonliteralString); } int scanf(char const *, ...); @@ -218,6 +223,7 @@ void test2(int intSAParm[static 2]) { // CHECK: printf("%LG", (long double) 42); // CHECK: printf("%La", (long double) 42); // CHECK: printf("%LA", (long double) 42); +// CHECK: printf("%s", NonliteralString); // CHECK: scanf("%99s", str); // CHECK: scanf("%s", vstr); diff --git a/test/SemaObjC/format-strings-objc-fixit.m b/test/SemaObjC/format-strings-objc-fixit.m new file mode 100644 index 00000000000..feaebeec57f --- /dev/null +++ b/test/SemaObjC/format-strings-objc-fixit.m @@ -0,0 +1,31 @@ +// RUN: cp %s %t +// RUN: %clang_cc1 -x objective-c -triple x86_64-apple-darwin -Wno-objc-root-class -pedantic -Wall -fixit %t +// RUN: %clang_cc1 -x objective-c -triple x86_64-apple-darwin -Wno-objc-root-class -fsyntax-only -pedantic -Wall -Werror %t +// RUN: %clang_cc1 -x objective-c -triple x86_64-apple-darwin -Wno-objc-root-class -E -o - %t | FileCheck %s + +typedef signed char BOOL; +typedef unsigned int NSUInteger; +typedef struct _NSZone NSZone; +@class NSCoder, NSString, NSEnumerator; +@protocol NSObject - (BOOL)isEqual:(id)object; @end +@protocol NSCopying - (id)copyWithZone:(NSZone *)zone; @end +@protocol NSMutableCopying - (id)mutableCopyWithZone:(NSZone *)zone; @end +@protocol NSCoding - (void)encodeWithCoder:(NSCoder *)aCoder; @end +@interface NSObject {} @end +@interface NSString : NSObject - (NSUInteger)length; @end +extern void NSLog(NSString *format, ...); + +/* This is a test of the various code modification hints that are + provided as part of warning or extension diagnostics. All of the + warnings will be fixed by -fixit, and the resulting file should + compile cleanly with -Werror -pedantic. */ + +extern NSString *NonliteralString; + +void test() { + // nonliteral format + NSLog(NonliteralString); +} + +// Validate the fixes. +// CHECK: NSLog(@"%@", NonliteralString); From 98356ff4d3d54e3fa9c6fe37e9b98e658d9ffb72 Mon Sep 17 00:00:00 2001 From: Bob Wilson Date: Tue, 15 Mar 2016 20:56:38 +0000 Subject: [PATCH 287/742] Move the fixit for -Wformat-security to a note. r263299 added a fixit for the -Wformat-security warning, but that runs into complications with our guideline that error recovery should be done as-if the fixit had been applied. Putting the fixit on a note avoids that. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263584 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 2 ++ lib/Sema/SemaChecking.cpp | 11 ++++---- test/Sema/format-strings-fixit.c | 6 ----- test/Sema/format-strings.c | 18 +++++++++++++ test/SemaCXX/format-strings-0x.cpp | 1 + test/SemaCXX/format-strings.cpp | 1 + test/SemaObjC/format-strings-objc-fixit.m | 31 ---------------------- test/SemaObjC/format-strings-objc.m | 1 + 8 files changed, 29 insertions(+), 42 deletions(-) delete mode 100644 test/SemaObjC/format-strings-objc-fixit.m diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index dd9fb576255..afc1a9553a7 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -7096,6 +7096,8 @@ def warn_scanf_scanlist_incomplete : Warning< def note_format_string_defined : Note<"format string is defined here">; def note_format_fix_specifier : Note<"did you mean to use '%0'?">; def note_printf_c_str: Note<"did you mean to call the %0 method?">; +def note_format_security_fixit: Note< + "treat the string as an argument to avoid this">; def warn_null_arg : Warning< "null passed to a callee that requires a non-null argument">, diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index 982772923ca..5fa6b640e82 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -3285,19 +3285,20 @@ bool Sema::CheckFormatArguments(ArrayRef Args, // If there are no arguments specified, warn with -Wformat-security, otherwise // warn only with -Wformat-nonliteral. if (Args.size() == firstDataArg) { - const SemaDiagnosticBuilder &D = - Diag(FormatLoc, diag::warn_format_nonliteral_noargs); + Diag(FormatLoc, diag::warn_format_nonliteral_noargs) + << OrigFormatExpr->getSourceRange(); switch (Type) { default: - D << OrigFormatExpr->getSourceRange(); break; case FST_Kprintf: case FST_FreeBSDKPrintf: case FST_Printf: - D << FixItHint::CreateInsertion(FormatLoc, "\"%s\", "); + Diag(FormatLoc, diag::note_format_security_fixit) + << FixItHint::CreateInsertion(FormatLoc, "\"%s\", "); break; case FST_NSString: - D << FixItHint::CreateInsertion(FormatLoc, "@\"%@\", "); + Diag(FormatLoc, diag::note_format_security_fixit) + << FixItHint::CreateInsertion(FormatLoc, "@\"%@\", "); break; } } else { diff --git a/test/Sema/format-strings-fixit.c b/test/Sema/format-strings-fixit.c index fb617dd500f..b982eb45e5f 100644 --- a/test/Sema/format-strings-fixit.c +++ b/test/Sema/format-strings-fixit.c @@ -16,8 +16,6 @@ typedef __UINTMAX_TYPE__ uintmax_t; typedef __PTRDIFF_TYPE__ ptrdiff_t; typedef __WCHAR_TYPE__ wchar_t; -extern const char *NonliteralString; - void test() { // Basic types printf("%s", (int) 123); @@ -96,9 +94,6 @@ void test() { printf("%G", (long double) 42); printf("%a", (long double) 42); printf("%A", (long double) 42); - - // nonliteral format - printf(NonliteralString); } int scanf(char const *, ...); @@ -223,7 +218,6 @@ void test2(int intSAParm[static 2]) { // CHECK: printf("%LG", (long double) 42); // CHECK: printf("%La", (long double) 42); // CHECK: printf("%LA", (long double) 42); -// CHECK: printf("%s", NonliteralString); // CHECK: scanf("%99s", str); // CHECK: scanf("%s", vstr); diff --git a/test/Sema/format-strings.c b/test/Sema/format-strings.c index 0d827e400d7..6ced9979298 100644 --- a/test/Sema/format-strings.c +++ b/test/Sema/format-strings.c @@ -29,15 +29,22 @@ void check_string_literal( FILE* fp, const char* s, char *buf, ... ) { va_start(ap,buf); printf(s); // expected-warning {{format string is not a string literal}} + // expected-note@-1{{treat the string as an argument to avoid this}} vprintf(s,ap); // expected-warning {{format string is not a string literal}} fprintf(fp,s); // expected-warning {{format string is not a string literal}} + // expected-note@-1{{treat the string as an argument to avoid this}} vfprintf(fp,s,ap); // expected-warning {{format string is not a string literal}} asprintf(&b,s); // expected-warning {{format string is not a string lit}} + // expected-note@-1{{treat the string as an argument to avoid this}} vasprintf(&b,s,ap); // expected-warning {{format string is not a string literal}} sprintf(buf,s); // expected-warning {{format string is not a string literal}} + // expected-note@-1{{treat the string as an argument to avoid this}} snprintf(buf,2,s); // expected-warning {{format string is not a string lit}} + // expected-note@-1{{treat the string as an argument to avoid this}} __builtin___sprintf_chk(buf,0,-1,s); // expected-warning {{format string is not a string literal}} + // expected-note@-1{{treat the string as an argument to avoid this}} __builtin___snprintf_chk(buf,2,0,-1,s); // expected-warning {{format string is not a string lit}} + // expected-note@-1{{treat the string as an argument to avoid this}} vsprintf(buf,s,ap); // expected-warning {{format string is not a string lit}} vsnprintf(buf,2,s,ap); // expected-warning {{format string is not a string lit}} vsnprintf(buf,2,global_fmt,ap); // expected-warning {{format string is not a string literal}} @@ -69,13 +76,18 @@ void check_string_literal2( FILE* fp, const char* s, char *buf, ... ) { va_start(ap,buf); printf(s); // expected-warning {{format string is not a string literal}} + // expected-note@-1{{treat the string as an argument to avoid this}} vprintf(s,ap); // no-warning fprintf(fp,s); // expected-warning {{format string is not a string literal}} + // expected-note@-1{{treat the string as an argument to avoid this}} vfprintf(fp,s,ap); // no-warning asprintf(&b,s); // expected-warning {{format string is not a string lit}} + // expected-note@-1{{treat the string as an argument to avoid this}} vasprintf(&b,s,ap); // no-warning sprintf(buf,s); // expected-warning {{format string is not a string literal}} + // expected-note@-1{{treat the string as an argument to avoid this}} snprintf(buf,2,s); // expected-warning {{format string is not a string lit}} + // expected-note@-1{{treat the string as an argument to avoid this}} __builtin___vsnprintf_chk(buf,2,0,-1,s,ap); // no-warning vscanf(s, ap); // expected-warning {{format string is not a string literal}} @@ -85,6 +97,7 @@ void check_conditional_literal(const char* s, int i) { printf(i == 1 ? "yes" : "no"); // no-warning printf(i == 0 ? (i == 1 ? "yes" : "no") : "dont know"); // no-warning printf(i == 0 ? (i == 1 ? s : "no") : "dont know"); // expected-warning{{format string is not a string literal}} + // expected-note@-1{{treat the string as an argument to avoid this}} printf("yes" ?: "no %d", 1); // expected-warning{{data argument not used by format string}} } @@ -185,8 +198,11 @@ void test_constant_bindings(void) { printf(s1); // no-warning printf(s2); // no-warning printf(s3); // expected-warning{{not a string literal}} + // expected-note@-1{{treat the string as an argument to avoid this}} printf(s4); // expected-warning{{not a string literal}} + // expected-note@-1{{treat the string as an argument to avoid this}} printf(s5); // expected-warning{{not a string literal}} + // expected-note@-1{{treat the string as an argument to avoid this}} } @@ -197,6 +213,7 @@ void test_constant_bindings(void) { void test9(char *P) { int x; printf(P); // expected-warning {{format string is not a string literal (potentially insecure)}} + // expected-note@-1{{treat the string as an argument to avoid this}} printf(P, 42); } @@ -615,5 +632,6 @@ extern void test_format_security_extra_args(const char*, int, ...) __attribute__((__format__(__printf__, 1, 3))); void test_format_security_pos(char* string) { test_format_security_extra_args(string, 5); // expected-warning {{format string is not a string literal (potentially insecure)}} + // expected-note@-1{{treat the string as an argument to avoid this}} } #pragma GCC diagnostic warning "-Wformat-nonliteral" diff --git a/test/SemaCXX/format-strings-0x.cpp b/test/SemaCXX/format-strings-0x.cpp index ad57b773e0a..7d37f8276f2 100644 --- a/test/SemaCXX/format-strings-0x.cpp +++ b/test/SemaCXX/format-strings-0x.cpp @@ -15,6 +15,7 @@ void f(char **sp, float *fp) { scanf("%afoobar", fp); printf(nullptr); printf(*sp); // expected-warning {{not a string literal}} + // expected-note@-1{{treat the string as an argument to avoid this}} // PR13099 printf( diff --git a/test/SemaCXX/format-strings.cpp b/test/SemaCXX/format-strings.cpp index fa7251d0dd7..b7ef1d709f2 100644 --- a/test/SemaCXX/format-strings.cpp +++ b/test/SemaCXX/format-strings.cpp @@ -54,6 +54,7 @@ void rdar8269537(const char *f) test_null_format(0); // no-warning test_null_format(__null); // no-warning test_null_format(f); // expected-warning {{not a string literal}} + // expected-note@-1{{treat the string as an argument to avoid this}} } int Foo::printf(const char *fmt, ...) { diff --git a/test/SemaObjC/format-strings-objc-fixit.m b/test/SemaObjC/format-strings-objc-fixit.m deleted file mode 100644 index feaebeec57f..00000000000 --- a/test/SemaObjC/format-strings-objc-fixit.m +++ /dev/null @@ -1,31 +0,0 @@ -// RUN: cp %s %t -// RUN: %clang_cc1 -x objective-c -triple x86_64-apple-darwin -Wno-objc-root-class -pedantic -Wall -fixit %t -// RUN: %clang_cc1 -x objective-c -triple x86_64-apple-darwin -Wno-objc-root-class -fsyntax-only -pedantic -Wall -Werror %t -// RUN: %clang_cc1 -x objective-c -triple x86_64-apple-darwin -Wno-objc-root-class -E -o - %t | FileCheck %s - -typedef signed char BOOL; -typedef unsigned int NSUInteger; -typedef struct _NSZone NSZone; -@class NSCoder, NSString, NSEnumerator; -@protocol NSObject - (BOOL)isEqual:(id)object; @end -@protocol NSCopying - (id)copyWithZone:(NSZone *)zone; @end -@protocol NSMutableCopying - (id)mutableCopyWithZone:(NSZone *)zone; @end -@protocol NSCoding - (void)encodeWithCoder:(NSCoder *)aCoder; @end -@interface NSObject {} @end -@interface NSString : NSObject - (NSUInteger)length; @end -extern void NSLog(NSString *format, ...); - -/* This is a test of the various code modification hints that are - provided as part of warning or extension diagnostics. All of the - warnings will be fixed by -fixit, and the resulting file should - compile cleanly with -Werror -pedantic. */ - -extern NSString *NonliteralString; - -void test() { - // nonliteral format - NSLog(NonliteralString); -} - -// Validate the fixes. -// CHECK: NSLog(@"%@", NonliteralString); diff --git a/test/SemaObjC/format-strings-objc.m b/test/SemaObjC/format-strings-objc.m index 079460cc76c..a1ebf03f8ef 100644 --- a/test/SemaObjC/format-strings-objc.m +++ b/test/SemaObjC/format-strings-objc.m @@ -116,6 +116,7 @@ void rdar10743758(id x) { NSLog(ns2); // expected-warning {{more '%' conversions than data arguments}} NSString * ns3 = ns1; NSLog(ns3); // expected-warning {{format string is not a string literal}}} + // expected-note@-1{{treat the string as an argument to avoid this}} NSString * const ns6 = @"split" " string " @"%s"; // expected-note {{format string is defined here}} NSLog(ns6); // expected-warning {{more '%' conversions than data arguments}} From 311666be46ca66b7fccc705b1d655b60537b6904 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Wed, 9 Mar 2016 02:53:12 +0000 Subject: [PATCH 288/742] [index] Add a message for the assertion, NFC. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262991 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Index/IndexingContext.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Index/IndexingContext.cpp b/lib/Index/IndexingContext.cpp index fe814ff25a6..238de57ccd4 100644 --- a/lib/Index/IndexingContext.cpp +++ b/lib/Index/IndexingContext.cpp @@ -299,7 +299,8 @@ bool IndexingContext::handleDeclOccurrence(const Decl *D, SourceLocation Loc, Parent = getCanonicalDecl(Parent); assert(!Parent || !Parent->isImplicit() || isa(Parent) || - isa(Parent) || isa(Parent)); + isa(Parent) || isa(Parent) && + "unexpected implicit parent!"); SmallVector FinalRelations; FinalRelations.reserve(Relations.size()+1); From c4a058fe549ec25fe682f5a447e82b4188b484b2 Mon Sep 17 00:00:00 2001 From: Manuel Klimek Date: Wed, 9 Mar 2016 10:06:45 +0000 Subject: [PATCH 289/742] Pacify gcc's parenthesis warning, which doesn't realize that parens don't matter here. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263004 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Index/IndexingContext.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/Index/IndexingContext.cpp b/lib/Index/IndexingContext.cpp index 238de57ccd4..0a235afc28b 100644 --- a/lib/Index/IndexingContext.cpp +++ b/lib/Index/IndexingContext.cpp @@ -297,9 +297,8 @@ bool IndexingContext::handleDeclOccurrence(const Decl *D, SourceLocation Loc, Parent = adjustParent(Parent); if (Parent) Parent = getCanonicalDecl(Parent); - assert(!Parent || !Parent->isImplicit() || - isa(Parent) || - isa(Parent) || isa(Parent) && + assert((!Parent || !Parent->isImplicit() || isa(Parent) || + isa(Parent) || isa(Parent)) && "unexpected implicit parent!"); SmallVector FinalRelations; From cae01ddb857378ed90bc5e0fc01a9afa3156f29e Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Thu, 17 Mar 2016 04:28:19 +0000 Subject: [PATCH 290/742] [index] Make sure that declarations of builtin functions are indexed. rdar://25154630 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263689 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Index/IndexingContext.cpp | 7 +++++-- test/Index/Core/index-source.m | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/Index/IndexingContext.cpp b/lib/Index/IndexingContext.cpp index 0a235afc28b..3d1d9902dd6 100644 --- a/lib/Index/IndexingContext.cpp +++ b/lib/Index/IndexingContext.cpp @@ -290,14 +290,17 @@ bool IndexingContext::handleDeclOccurrence(const Decl *D, SourceLocation Loc, Roles |= (unsigned)SymbolRole::Declaration; D = getCanonicalDecl(D); - if (D->isImplicit() && !isa(D)) { + if (D->isImplicit() && !isa(D) && + !(isa(D) && cast(D)->getBuiltinID())) { // operator new declarations will link to the implicit one as canonical. return true; } Parent = adjustParent(Parent); if (Parent) Parent = getCanonicalDecl(Parent); - assert((!Parent || !Parent->isImplicit() || isa(Parent) || + assert((!Parent || !Parent->isImplicit() || + (isa(Parent) && + cast(Parent)->getBuiltinID()) || isa(Parent) || isa(Parent)) && "unexpected implicit parent!"); diff --git a/test/Index/Core/index-source.m b/test/Index/Core/index-source.m index 6248f18bfb5..ac309c8e696 100644 --- a/test/Index/Core/index-source.m +++ b/test/Index/Core/index-source.m @@ -68,5 +68,6 @@ -(ObjectType)getit; // CHECK: [[@LINE+1]]:13 | typedef/C | jmp_buf | c:index-source.m@T@jmp_buf | | Def | rel: 0 typedef int jmp_buf[(18)]; +// CHECK: [[@LINE+2]]:12 | function/C | setjmp | c:@F@setjmp | _setjmp | Decl | rel: 0 // CHECK: [[@LINE+1]]:19 | typedef/C | jmp_buf | c:index-source.m@T@jmp_buf | | Ref | rel: 0 extern int setjmp(jmp_buf); From 3e4d01d89ba6b81d176ab6add7531163df2a3262 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 17 Mar 2016 13:39:41 -0700 Subject: [PATCH 291/742] [Swift] swift_bridge can apply to ObjCInterfaceDecls, of course. Clang's architectural failure of ObjCInterfaceDecl not being a TypeDecl continues to surprise us. --- include/clang/Basic/Attr.td | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 63748c38795..9daf3a766e3 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -1311,8 +1311,8 @@ def Regparm : TypeAttr { def SwiftBridge : Attr { let Spellings = [GNU<"swift_bridge">]; - let Subjects = SubjectList<[Tag, TypedefName, ObjCProtocol], ErrorDiag, - "ExpectedType">; + let Subjects = SubjectList<[Tag, TypedefName, ObjCInterface, ObjCProtocol], + ErrorDiag, "ExpectedType">; let Args = [StringArgument<"SwiftType">]; let Documentation = [SwiftBridgeDocs]; } From d5617b44cd06e963eae3bf5ac472a84cf88d5a87 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 17 Mar 2016 13:39:41 -0700 Subject: [PATCH 292/742] [Swift] swift_bridge can apply to ObjCInterfaceDecls, of course. Clang's architectural failure of ObjCInterfaceDecl not being a TypeDecl continues to surprise us. --- include/clang/Basic/Attr.td | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 2a531e788ae..0426ebfbc59 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -1312,8 +1312,8 @@ def Regparm : TypeAttr { def SwiftBridge : Attr { let Spellings = [GNU<"swift_bridge">]; - let Subjects = SubjectList<[Tag, TypedefName, ObjCProtocol], ErrorDiag, - "ExpectedType">; + let Subjects = SubjectList<[Tag, TypedefName, ObjCInterface, ObjCProtocol], + ErrorDiag, "ExpectedType">; let Args = [StringArgument<"SwiftType">]; let Documentation = [SwiftBridgeDocs]; } From cce9fe9fe6eaf233ba0049a65f6b1936d7538b57 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Fri, 18 Mar 2016 19:03:50 +0000 Subject: [PATCH 293/742] [Objective-c] Fix a crash in WeakObjectProfileTy::getBaseInfo. The crash occurs in WeakObjectProfileTy::getBaseInfo when getBase() is called on an ObjCPropertyRefExpr object whose receiver is an interface. This commit fixes the crash by checking the type of the receiver and setting IsExact to true if it is an interface. rdar://problem/25208167 Differential Revision: http://reviews.llvm.org/D18268 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263818 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 1d22cda9dbf116c761db465ee2c7ce50acb7863d) --- lib/Sema/ScopeInfo.cpp | 14 +++++++++----- test/SemaObjC/arc-repeated-weak.mm | 12 ++++++++++++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/lib/Sema/ScopeInfo.cpp b/lib/Sema/ScopeInfo.cpp index cbd7ef7abb4..144b3a5c86f 100644 --- a/lib/Sema/ScopeInfo.cpp +++ b/lib/Sema/ScopeInfo.cpp @@ -85,11 +85,15 @@ FunctionScopeInfo::WeakObjectProfileTy::getBaseInfo(const Expr *E) { if (BaseProp) { D = getBestPropertyDecl(BaseProp); - const Expr *DoubleBase = BaseProp->getBase(); - if (const OpaqueValueExpr *OVE = dyn_cast(DoubleBase)) - DoubleBase = OVE->getSourceExpr(); - - IsExact = DoubleBase->isObjCSelfExpr(); + if (BaseProp->isClassReceiver()) + IsExact = true; + else { + const Expr *DoubleBase = BaseProp->getBase(); + if (const OpaqueValueExpr *OVE = dyn_cast(DoubleBase)) + DoubleBase = OVE->getSourceExpr(); + + IsExact = DoubleBase->isObjCSelfExpr(); + } } break; } diff --git a/test/SemaObjC/arc-repeated-weak.mm b/test/SemaObjC/arc-repeated-weak.mm index 264c598942a..7ac2313fa31 100644 --- a/test/SemaObjC/arc-repeated-weak.mm +++ b/test/SemaObjC/arc-repeated-weak.mm @@ -439,3 +439,15 @@ - (void) Meth : (id) data } @end +// This used to crash in WeakObjectProfileTy::getBaseInfo when getBase() was +// called on an ObjCPropertyRefExpr object whose receiver was an interface. + +@class NSString; +@interface NSBundle ++(NSBundle *)foo; +@property NSString *prop; +@end + +void foo() { + NSString * t = NSBundle.foo.prop; +} From 08115fd693a72a5e6a282951efdf74835b566876 Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Fri, 18 Mar 2016 23:35:21 +0000 Subject: [PATCH 294/742] [TLS on Darwin] use CXX_FAST_TLS calling convention for tls_init. This makes sure we don't generate a lot of code to spill/reload CSRs when calling tls_init from the access functions. This helps performance when tls_init is not inlined into the access functions. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263854 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/ItaniumCXXABI.cpp | 12 ++++++++++-- test/CodeGenCXX/cxx11-thread-local-reference.cpp | 6 ++++-- test/CodeGenCXX/cxx11-thread-local.cpp | 12 ++++++++---- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp index 753bf561e9b..b339e3eac11 100644 --- a/lib/CodeGen/ItaniumCXXABI.cpp +++ b/lib/CodeGen/ItaniumCXXABI.cpp @@ -2240,6 +2240,11 @@ void ItaniumCXXABI::EmitThreadLocalInitFuncs( CodeGenFunction(CGM) .GenerateCXXGlobalInitFunc(InitFunc, CXXThreadLocalInits, Address(Guard, GuardAlign)); + // On Darwin platforms, use CXX_FAST_TLS calling convention. + if (CGM.getTarget().getTriple().isOSDarwin()) { + InitFunc->setCallingConv(llvm::CallingConv::CXX_FAST_TLS); + InitFunc->addFnAttr(llvm::Attribute::NoUnwind); + } } for (const VarDecl *VD : CXXThreadLocals) { llvm::GlobalVariable *Var = @@ -2291,8 +2296,11 @@ void ItaniumCXXABI::EmitThreadLocalInitFuncs( llvm::BasicBlock *Entry = llvm::BasicBlock::Create(Context, "", Wrapper); CGBuilderTy Builder(CGM, Entry); if (InitIsInitFunc) { - if (Init) - Builder.CreateCall(Init); + if (Init) { + llvm::CallInst *CallVal = Builder.CreateCall(Init); + if (isThreadWrapperReplaceable(VD, CGM)) + CallVal->setCallingConv(llvm::CallingConv::CXX_FAST_TLS); + } } else { // Don't know whether we have an init function. Call it if it exists. llvm::Value *Have = Builder.CreateIsNotNull(Init); diff --git a/test/CodeGenCXX/cxx11-thread-local-reference.cpp b/test/CodeGenCXX/cxx11-thread-local-reference.cpp index 048a0bd45ad..4c1e5a70cba 100644 --- a/test/CodeGenCXX/cxx11-thread-local-reference.cpp +++ b/test/CodeGenCXX/cxx11-thread-local-reference.cpp @@ -23,11 +23,13 @@ int &g() { return r; } // LINUX: define weak_odr hidden i32* @_ZTW1r() [[ATTR0:#[0-9]+]] { // DARWIN: define cxx_fast_tlscc i32* @_ZTW1r() [[ATTR1:#[0-9]+]] { -// CHECK: call void @_ZTH1r() +// LINUX: call void @_ZTH1r() +// DARWIN: call cxx_fast_tlscc void @_ZTH1r() // CHECK: load i32*, i32** @r, align 8 // CHECK: ret i32* %{{.*}} -// CHECK-LABEL: define internal void @__tls_init() +// LINUX-LABEL: define internal void @__tls_init() +// DARWIN-LABEL: define internal cxx_fast_tlscc void @__tls_init() // CHECK: call void @[[R_INIT]]() // LINUX: attributes [[ATTR0]] = { {{.*}}"target-features"{{.*}} } diff --git a/test/CodeGenCXX/cxx11-thread-local.cpp b/test/CodeGenCXX/cxx11-thread-local.cpp index 40f33e0848b..f465cbdeea8 100644 --- a/test/CodeGenCXX/cxx11-thread-local.cpp +++ b/test/CodeGenCXX/cxx11-thread-local.cpp @@ -122,7 +122,8 @@ int f() { // LINUX-LABEL: define weak_odr hidden i32* @_ZTWN1VIiE1mE() // DARWIN-LABEL: define weak_odr hidden cxx_fast_tlscc i32* @_ZTWN1VIiE1mE() -// CHECK: call void @_ZTHN1VIiE1mE() +// LINUX: call void @_ZTHN1VIiE1mE() +// DARWIN: call cxx_fast_tlscc void @_ZTHN1VIiE1mE() // CHECK: ret i32* @_ZN1VIiE1mE @@ -212,7 +213,8 @@ void set_anon_i() { // LIUNX: define weak_odr hidden i32* @_ZTW1a() { // DARWIN: define cxx_fast_tlscc i32* @_ZTW1a() -// CHECK: call void @_ZTH1a() +// LINUX: call void @_ZTH1a() +// DARWIN: call cxx_fast_tlscc void @_ZTH1a() // CHECK: ret i32* @a // CHECK: } @@ -222,12 +224,14 @@ void set_anon_i() { // LINUX-LABEL: define internal i32* @_ZTWL1d() // DARWIN-LABEL: define internal cxx_fast_tlscc i32* @_ZTWL1d() -// CHECK: call void @_ZTHL1d() +// LINUX: call void @_ZTHL1d() +// DARWIN: call cxx_fast_tlscc void @_ZTHL1d() // CHECK: ret i32* @_ZL1d // LINUX-LABEL: define weak_odr hidden i32* @_ZTWN1U1mE() // DARWIN-LABEL: define cxx_fast_tlscc i32* @_ZTWN1U1mE() -// CHECK: call void @_ZTHN1U1mE() +// LINUX: call void @_ZTHN1U1mE() +// DARWIN: call cxx_fast_tlscc void @_ZTHN1U1mE() // CHECK: ret i32* @_ZN1U1mE // LINUX: attributes [[ATTR]] = { {{.+}} } From 97af164e2d2a0c84eb23cd08d1ebd5e823a2be59 Mon Sep 17 00:00:00 2001 From: Mehdi Amini Date: Fri, 11 Mar 2016 17:15:44 +0000 Subject: [PATCH 295/742] Remove compile time PreserveName switch based on NDEBUG Summary: Following r263086, we are now relying on a flag on the Context to discard Value names in release builds. Reviewers: chandlerc Subscribers: cfe-commits Differential Revision: http://reviews.llvm.org/D18024 From: Mehdi Amini git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263257 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CGBuilder.h | 19 ++++--------------- lib/CodeGen/CGCall.cpp | 2 +- lib/CodeGen/CGExpr.cpp | 2 -- lib/CodeGen/CodeGenAction.cpp | 8 +++++++- lib/CodeGen/CodeGenFunction.cpp | 20 +++----------------- 5 files changed, 15 insertions(+), 36 deletions(-) diff --git a/lib/CodeGen/CGBuilder.h b/lib/CodeGen/CGBuilder.h index 489f3413d4b..d6fc1488677 100644 --- a/lib/CodeGen/CGBuilder.h +++ b/lib/CodeGen/CGBuilder.h @@ -22,9 +22,7 @@ class CodeGenFunction; /// \brief This is an IRBuilder insertion helper that forwards to /// CodeGenFunction::InsertHelper, which adds necessary metadata to /// instructions. -template -class CGBuilderInserter - : protected llvm::IRBuilderDefaultInserter { +class CGBuilderInserter : protected llvm::IRBuilderDefaultInserter { public: CGBuilderInserter() = default; explicit CGBuilderInserter(CodeGenFunction *CGF) : CGF(CGF) {} @@ -38,17 +36,10 @@ class CGBuilderInserter CodeGenFunction *CGF = nullptr; }; -// Don't preserve names on values in an optimized build. -#ifdef NDEBUG -#define PreserveNames false -#else -#define PreserveNames true -#endif - -typedef CGBuilderInserter CGBuilderInserterTy; +typedef CGBuilderInserter CGBuilderInserterTy; -typedef llvm::IRBuilder CGBuilderBaseTy; +typedef llvm::IRBuilder + CGBuilderBaseTy; class CGBuilderTy : public CGBuilderBaseTy { /// Storing a reference to the type cache here makes it a lot easier @@ -298,8 +289,6 @@ class CGBuilderTy : public CGBuilderBaseTy { } }; -#undef PreserveNames - } // end namespace CodeGen } // end namespace clang diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index 645dc5ae767..1fcb934f05e 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -3583,7 +3583,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, } llvm::Instruction *CI = CS.getInstruction(); - if (Builder.isNamePreserving() && !CI->getType()->isVoidTy()) + if (!CI->getType()->isVoidTy()) CI->setName("call"); // Emit any writebacks immediately. Arguably this should happen diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index 7d1c77a14f9..e68f151f8aa 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/lib/CodeGen/CGExpr.cpp @@ -66,8 +66,6 @@ Address CodeGenFunction::CreateTempAlloca(llvm::Type *Ty, CharUnits Align, /// block. llvm::AllocaInst *CodeGenFunction::CreateTempAlloca(llvm::Type *Ty, const Twine &Name) { - if (!Builder.isNamePreserving()) - return new llvm::AllocaInst(Ty, nullptr, "", AllocaInsertPt); return new llvm::AllocaInst(Ty, nullptr, Name, AllocaInsertPt); } diff --git a/lib/CodeGen/CodeGenAction.cpp b/lib/CodeGen/CodeGenAction.cpp index cdd9d09d048..fac69ba447e 100644 --- a/lib/CodeGen/CodeGenAction.cpp +++ b/lib/CodeGen/CodeGenAction.cpp @@ -625,7 +625,13 @@ void BackendConsumer::DiagnosticHandlerImpl(const DiagnosticInfo &DI) { CodeGenAction::CodeGenAction(unsigned _Act, LLVMContext *_VMContext) : Act(_Act), VMContext(_VMContext ? _VMContext : new LLVMContext), - OwnsVMContext(!_VMContext) {} + OwnsVMContext(!_VMContext) { +#ifdef NDEBUG + // FIXME: change this to be controlled by a cc1 flag that the driver passes, + // on the model of --disable-free + _VMContext.setDiscardValueNames(true); +#endif +} CodeGenAction::~CodeGenAction() { TheModule.reset(); diff --git a/lib/CodeGen/CodeGenFunction.cpp b/lib/CodeGen/CodeGenFunction.cpp index e079b5869d9..84edc92107f 100644 --- a/lib/CodeGen/CodeGenFunction.cpp +++ b/lib/CodeGen/CodeGenFunction.cpp @@ -745,9 +745,7 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, // later. Don't create this with the builder, because we don't want it // folded. llvm::Value *Undef = llvm::UndefValue::get(Int32Ty); - AllocaInsertPt = new llvm::BitCastInst(Undef, Int32Ty, "", EntryBB); - if (Builder.isNamePreserving()) - AllocaInsertPt->setName("allocapt"); + AllocaInsertPt = new llvm::BitCastInst(Undef, Int32Ty, "allocapt", EntryBB); ReturnBlock = getJumpDestInCurrentScope("return"); @@ -1860,26 +1858,14 @@ void CodeGenFunction::InsertHelper(llvm::Instruction *I, CGM.getSanitizerMetadata()->disableSanitizerForInstruction(I); } -template -void CGBuilderInserter::InsertHelper( +void CGBuilderInserter::InsertHelper( llvm::Instruction *I, const llvm::Twine &Name, llvm::BasicBlock *BB, llvm::BasicBlock::iterator InsertPt) const { - llvm::IRBuilderDefaultInserter::InsertHelper(I, Name, BB, - InsertPt); + llvm::IRBuilderDefaultInserter::InsertHelper(I, Name, BB, InsertPt); if (CGF) CGF->InsertHelper(I, Name, BB, InsertPt); } -#ifdef NDEBUG -#define PreserveNames false -#else -#define PreserveNames true -#endif -template void CGBuilderInserter::InsertHelper( - llvm::Instruction *I, const llvm::Twine &Name, llvm::BasicBlock *BB, - llvm::BasicBlock::iterator InsertPt) const; -#undef PreserveNames - static bool hasRequiredFeatures(const SmallVectorImpl &ReqFeatures, CodeGenModule &CGM, const FunctionDecl *FD, std::string &FirstMissing) { From 3baf48c677b8afd92b22cb819cf0283fa8e70cf8 Mon Sep 17 00:00:00 2001 From: Mehdi Amini Date: Fri, 11 Mar 2016 17:32:58 +0000 Subject: [PATCH 296/742] Fix build: use -> with pointers and not . Silly typo. From: Mehdi Amini git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263267 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CodeGenAction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CodeGen/CodeGenAction.cpp b/lib/CodeGen/CodeGenAction.cpp index fac69ba447e..b8faf8b1735 100644 --- a/lib/CodeGen/CodeGenAction.cpp +++ b/lib/CodeGen/CodeGenAction.cpp @@ -629,7 +629,7 @@ CodeGenAction::CodeGenAction(unsigned _Act, LLVMContext *_VMContext) #ifdef NDEBUG // FIXME: change this to be controlled by a cc1 flag that the driver passes, // on the model of --disable-free - _VMContext.setDiscardValueNames(true); + _VMContext->setDiscardValueNames(true); #endif } From c18ed3735dac799123d6d3a121d72cacec46a313 Mon Sep 17 00:00:00 2001 From: Mehdi Amini Date: Fri, 11 Mar 2016 18:48:02 +0000 Subject: [PATCH 297/742] Fix clang crash: when CodeGenAction is initialized without a context, use the member and not the parameter From: Mehdi Amini git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263273 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CodeGenAction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CodeGen/CodeGenAction.cpp b/lib/CodeGen/CodeGenAction.cpp index b8faf8b1735..cdae0884db8 100644 --- a/lib/CodeGen/CodeGenAction.cpp +++ b/lib/CodeGen/CodeGenAction.cpp @@ -629,7 +629,7 @@ CodeGenAction::CodeGenAction(unsigned _Act, LLVMContext *_VMContext) #ifdef NDEBUG // FIXME: change this to be controlled by a cc1 flag that the driver passes, // on the model of --disable-free - _VMContext->setDiscardValueNames(true); + VMContext->setDiscardValueNames(true); #endif } From fde01458bc9a9f6e394126827fb5dc005d5f2d34 Mon Sep 17 00:00:00 2001 From: mehdi_amini Date: Sun, 13 Mar 2016 21:05:23 +0000 Subject: [PATCH 298/742] Remove compile time PreserveName in favor of a runtime cc1 -discard-value-names option Summary: This flag is enabled by default in the driver when NDEBUG is set. It is forwarded on the LLVMContext to discard all value names (but GlobalValue) for performance purpose. This an improved version of D18024 Reviewers: echristo, chandlerc Subscribers: cfe-commits Differential Revision: http://reviews.llvm.org/D18127 From: Mehdi Amini git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263394 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Driver/CC1Options.td | 2 ++ include/clang/Frontend/CodeGenOptions.def | 1 + lib/CodeGen/ModuleBuilder.cpp | 5 +++-- lib/Driver/Tools.cpp | 2 ++ lib/Frontend/CompilerInvocation.cpp | 1 + test/CodeGen/mips-byval-arg.c | 4 ++-- test/CodeGen/mips-vector-arg.c | 4 ++-- test/CodeGen/mips-zero-sized-struct.c | 12 ++++++------ test/CodeGen/mips64-class-return.cpp | 2 +- test/CodeGen/mips64-padding-arg.c | 6 +++--- test/CodeGenCXX/debug-info-class.cpp | 6 +++--- test/CodeGenCXX/discard-name-values.cpp | 10 ++++++++++ test/CodeGenCXX/stack-reuse.cpp | 4 ++-- 13 files changed, 38 insertions(+), 21 deletions(-) create mode 100644 test/CodeGenCXX/discard-name-values.cpp diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td index 837fbff801b..be333c3c1e7 100644 --- a/include/clang/Driver/CC1Options.td +++ b/include/clang/Driver/CC1Options.td @@ -353,6 +353,8 @@ def code_completion_brief_comments : Flag<["-"], "code-completion-brief-comments HelpText<"Include brief documentation comments in code-completion results.">; def disable_free : Flag<["-"], "disable-free">, HelpText<"Disable freeing of memory on exit">; +def discard_value_names : Flag<["-"], "discard-value-names">, + HelpText<"Discard value names in LLVM IR">; def load : Separate<["-"], "load">, MetaVarName<"">, HelpText<"Load the named plugin (dynamic shared object)">; def plugin : Separate<["-"], "plugin">, MetaVarName<"">, diff --git a/include/clang/Frontend/CodeGenOptions.def b/include/clang/Frontend/CodeGenOptions.def index ba4cb520a82..fa707e38e23 100644 --- a/include/clang/Frontend/CodeGenOptions.def +++ b/include/clang/Frontend/CodeGenOptions.def @@ -43,6 +43,7 @@ CODEGENOPT(DataSections , 1, 0) ///< Set when -fdata-sections is enabled. CODEGENOPT(UniqueSectionNames, 1, 1) ///< Set for -funique-section-names. CODEGENOPT(DisableFPElim , 1, 0) ///< Set when -fomit-frame-pointer is enabled. CODEGENOPT(DisableFree , 1, 0) ///< Don't free memory. +CODEGENOPT(DiscardValueNames , 1, 0) ///< Discard Value Names from the IR (LLVMContext flag) CODEGENOPT(DisableGCov , 1, 0) ///< Don't run the GCov pass, for testing. CODEGENOPT(DisableLLVMOpts , 1, 0) ///< Don't run any optimizations, for use in ///< getting .bc files that correspond to the diff --git a/lib/CodeGen/ModuleBuilder.cpp b/lib/CodeGen/ModuleBuilder.cpp index 82ad0dbabb9..1d5f36eccb3 100644 --- a/lib/CodeGen/ModuleBuilder.cpp +++ b/lib/CodeGen/ModuleBuilder.cpp @@ -64,8 +64,9 @@ namespace { CoverageSourceInfo *CoverageInfo = nullptr) : Diags(diags), Ctx(nullptr), HeaderSearchOpts(HSO), PreprocessorOpts(PPO), CodeGenOpts(CGO), HandlingTopLevelDecls(0), - CoverageInfo(CoverageInfo), - M(new llvm::Module(ModuleName, C)) {} + CoverageInfo(CoverageInfo), M(new llvm::Module(ModuleName, C)) { + C.setDiscardValueNames(CGO.DiscardValueNames); + } ~CodeGeneratorImpl() override { // There should normally not be any leftover inline method definitions. diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index 5ec03e15812..c8ac070da10 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -3623,6 +3623,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, // Disable the verification pass in -asserts builds. #ifdef NDEBUG CmdArgs.push_back("-disable-llvm-verifier"); + // Discard LLVM value names in -asserts builds. + CmdArgs.push_back("-discard-value-names"); #endif // Set the main file name, so that debug info works even with diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index e9037e8444b..84c3abb51fd 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -493,6 +493,7 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, Opts.DisableFPElim = (Args.hasArg(OPT_mdisable_fp_elim) || Args.hasArg(OPT_pg)); Opts.DisableFree = Args.hasArg(OPT_disable_free); + Opts.DiscardValueNames = Args.hasArg(OPT_discard_value_names); Opts.DisableTailCalls = Args.hasArg(OPT_mdisable_tail_calls); Opts.FloatABI = Args.getLastArgValue(OPT_mfloat_abi); if (Arg *A = Args.getLastArg(OPT_meabi)) { diff --git a/test/CodeGen/mips-byval-arg.c b/test/CodeGen/mips-byval-arg.c index 0e3d334b274..1e7f38915cc 100644 --- a/test/CodeGen/mips-byval-arg.c +++ b/test/CodeGen/mips-byval-arg.c @@ -1,5 +1,5 @@ -// RUN: %clang -target mipsel-unknown-linux -O3 -S -o - -emit-llvm %s | FileCheck %s -check-prefix=O32 -// RUN: %clang -target mips64el-unknown-linux -O3 -S -mabi=n64 -o - -emit-llvm %s | FileCheck %s -check-prefix=N64 +// RUN: %clang_cc1 -triple mipsel-unknown-linux -O3 -S -o - -emit-llvm %s | FileCheck %s -check-prefix=O32 +// RUN: %clang_cc1 -triple mips64el-unknown-linux -O3 -S -target-abi n64 -o - -emit-llvm %s | FileCheck %s -check-prefix=N64 typedef struct { float f[3]; diff --git a/test/CodeGen/mips-vector-arg.c b/test/CodeGen/mips-vector-arg.c index f8c89dfff54..5e55285f3f1 100644 --- a/test/CodeGen/mips-vector-arg.c +++ b/test/CodeGen/mips-vector-arg.c @@ -1,5 +1,5 @@ -// RUN: %clang -target mipsel-unknown-linux -O3 -S -o - -emit-llvm %s | FileCheck %s -check-prefix=O32 -// RUN: %clang -target mips64el-unknown-linux -O3 -S -mabi=n64 -o - -emit-llvm %s | FileCheck %s -check-prefix=N64 +// RUN: %clang_cc1 -triple mipsel-unknown-linux -O3 -S -o - -emit-llvm %s | FileCheck %s -check-prefix=O32 +// RUN: %clang_cc1 -triple mips64el-unknown-linux -O3 -S -target-abi n64 -o - -emit-llvm %s | FileCheck %s -check-prefix=N64 // check that // 1. vector arguments are passed in integer registers diff --git a/test/CodeGen/mips-zero-sized-struct.c b/test/CodeGen/mips-zero-sized-struct.c index afff3b41d83..217bdb92c6f 100644 --- a/test/CodeGen/mips-zero-sized-struct.c +++ b/test/CodeGen/mips-zero-sized-struct.c @@ -1,9 +1,9 @@ -// RUN: %clang -target mips-unknown-linux-gnu -S -emit-llvm -o - %s | FileCheck -check-prefix=O32 %s -// RUN: %clang -target mipsel-unknown-linux-gnu -S -emit-llvm -o - %s | FileCheck -check-prefix=O32 %s -// RUN: %clang -target mips64-unknown-linux-gnu -S -emit-llvm -o - %s -mabi=n32 | FileCheck -check-prefix=N32 %s -// RUN: %clang -target mips64el-unknown-linux-gnu -S -emit-llvm -o - %s -mabi=n32 | FileCheck -check-prefix=N32 %s -// RUN: %clang -target mips64-unknown-linux-gnu -S -emit-llvm -o - %s | FileCheck -check-prefix=N64 %s -// RUN: %clang -target mips64el-unknown-linux-gnu -S -emit-llvm -o - %s | FileCheck -check-prefix=N64 %s +// RUN: %clang_cc1 -triple mips-unknown-linux-gnu -S -emit-llvm -o - %s | FileCheck -check-prefix=O32 %s +// RUN: %clang_cc1 -triple mipsel-unknown-linux-gnu -S -emit-llvm -o - %s | FileCheck -check-prefix=O32 %s +// RUN: %clang_cc1 -triple mips64-unknown-linux-gnu -S -emit-llvm -o - %s -target-abi n32 | FileCheck -check-prefix=N32 %s +// RUN: %clang_cc1 -triple mips64el-unknown-linux-gnu -S -emit-llvm -o - %s -target-abi n32 | FileCheck -check-prefix=N32 %s +// RUN: %clang_cc1 -triple mips64-unknown-linux-gnu -S -emit-llvm -o - %s | FileCheck -check-prefix=N64 %s +// RUN: %clang_cc1 -triple mips64el-unknown-linux-gnu -S -emit-llvm -o - %s | FileCheck -check-prefix=N64 %s // O32: define void @fn28(%struct.T2* noalias sret %agg.result, i8 signext %arg0) // N32: define void @fn28(i8 signext %arg0) diff --git a/test/CodeGen/mips64-class-return.cpp b/test/CodeGen/mips64-class-return.cpp index 57fa8ef5109..af2dd5cbec2 100644 --- a/test/CodeGen/mips64-class-return.cpp +++ b/test/CodeGen/mips64-class-return.cpp @@ -1,4 +1,4 @@ -// RUN: %clang -target mips64el-unknown-linux -O3 -S -mabi=n64 -o - -emit-llvm %s | FileCheck %s +// RUN: %clang_cc1 -triple mips64el-unknown-linux -O3 -S -target-abi n64 -o - -emit-llvm %s | FileCheck %s class B0 { double d; diff --git a/test/CodeGen/mips64-padding-arg.c b/test/CodeGen/mips64-padding-arg.c index b92098f45a4..7910734d6f1 100644 --- a/test/CodeGen/mips64-padding-arg.c +++ b/test/CodeGen/mips64-padding-arg.c @@ -1,6 +1,6 @@ -// RUN: %clang -target mipsel-unknown-linux -O3 -S -o - -emit-llvm %s | FileCheck %s -check-prefix=O32 -// RUN: %clang -target mips64el-unknown-linux -O3 -S -mabi=n64 -o - -emit-llvm %s | FileCheck %s -check-prefix=N64 -// RUN: %clang -target mipsel-unknown-linux -mfp64 -O3 -S -o - -emit-llvm %s | FileCheck %s -check-prefix=O32 +// RUN: %clang_cc1 -triple mipsel-unknown-linux -O3 -S -o - -emit-llvm %s | FileCheck %s -check-prefix=O32 +// RUN: %clang_cc1 -triple mips64el-unknown-linux -O3 -S -target-abi n64 -o - -emit-llvm %s | FileCheck %s -check-prefix=N64 +// RUN: %clang_cc1 -triple mipsel-unknown-linux -target-feature "+fp64" -O3 -S -o - -emit-llvm %s | FileCheck %s -check-prefix=O32 typedef struct { double d; diff --git a/test/CodeGenCXX/debug-info-class.cpp b/test/CodeGenCXX/debug-info-class.cpp index a63efe5d780..ed52fd05238 100644 --- a/test/CodeGenCXX/debug-info-class.cpp +++ b/test/CodeGenCXX/debug-info-class.cpp @@ -83,9 +83,9 @@ int main(int argc, char **argv) { return 0; } -// RUN: %clang -target x86_64-unknown_unknown -emit-llvm -g -S %s -o - | FileCheck %s -// RUN: %clang -target i686-cygwin -emit-llvm -g -S %s -o - | FileCheck %s -// RUN: %clang -target armv7l-unknown-linux-gnueabihf -emit-llvm -g -S %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown_unknown -emit-llvm -debug-info-kind=limited -fexceptions %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple i686-cygwin -emit-llvm -debug-info-kind=limited -fexceptions %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple armv7l-unknown-linux-gnueabihf -emit-llvm -debug-info-kind=limited -fexceptions %s -o - | FileCheck %s // CHECK: invoke {{.+}} @_ZN1BD1Ev(%class.B* %b) // CHECK-NEXT: unwind label %{{.+}}, !dbg ![[EXCEPTLOC:.*]] diff --git a/test/CodeGenCXX/discard-name-values.cpp b/test/CodeGenCXX/discard-name-values.cpp new file mode 100644 index 00000000000..49cb7d2fc05 --- /dev/null +++ b/test/CodeGenCXX/discard-name-values.cpp @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -emit-llvm -triple=armv7-apple-darwin -emit-llvm -std=c++11 %s -o - -O1 | FileCheck %s +// RUN: %clang_cc1 -emit-llvm -triple=armv7-apple-darwin -emit-llvm -std=c++11 %s -o - -O1 -discard-value-names | FileCheck %s --check-prefix=DISCARDVALUE + +int foo(int bar) { + return bar; +} + +// CHECK: ret i32 %bar +// DISCARDVALUE: ret i32 %0 + diff --git a/test/CodeGenCXX/stack-reuse.cpp b/test/CodeGenCXX/stack-reuse.cpp index 473a57cda27..d6340ef2c10 100644 --- a/test/CodeGenCXX/stack-reuse.cpp +++ b/test/CodeGenCXX/stack-reuse.cpp @@ -1,4 +1,4 @@ -// RUN: %clang -target armv7l-unknown-linux-gnueabihf -S %s -o - -emit-llvm -O1 -disable-llvm-optzns | FileCheck %s +// RUN: %clang_cc1 -triple armv7-unknown-linux-gnueabihf %s -o - -emit-llvm -O1 | FileCheck %s // Stack should be reused when possible, no need to allocate two separate slots // if they have disjoint lifetime. @@ -21,7 +21,7 @@ struct Combiner { S_large a, b; Combiner(S_large); - Combiner f(); + Combiner f(); }; extern S_small foo_small(); From 857025b72f3496ac93bb102f9f265592401117b1 Mon Sep 17 00:00:00 2001 From: Teresa Johnson Date: Fri, 11 Mar 2016 18:52:42 +0000 Subject: [PATCH 299/742] Update test case for llvm summary format changes in D17592. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263276 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/Misc/thinlto.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/Misc/thinlto.c b/test/Misc/thinlto.c index 9134cbe5c5e..ae2c148890d 100644 --- a/test/Misc/thinlto.c +++ b/test/Misc/thinlto.c @@ -1,8 +1,8 @@ // RUN: %clang_cc1 -flto=thin -emit-llvm-bc < %s | llvm-bcanalyzer -dump | FileCheck %s -// CHECK: Date: Tue, 15 Mar 2016 00:04:44 +0000 Subject: [PATCH 300/742] [ThinLTO] Clang side of renaming of function index (NFC) This is the companion to an LLVM patch that renamed the function index data structures and files to use the more general module summary index. (Recommit after fixing LLVM side to add back missed file) git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263514 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/CodeGen/BackendUtil.h | 2 +- include/clang/Frontend/CodeGenOptions.def | 4 +-- lib/CodeGen/BackendUtil.cpp | 36 +++++++++++------------ lib/Frontend/CompilerInvocation.cpp | 2 +- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/include/clang/CodeGen/BackendUtil.h b/include/clang/CodeGen/BackendUtil.h index d375a788131..7df1c041081 100644 --- a/include/clang/CodeGen/BackendUtil.h +++ b/include/clang/CodeGen/BackendUtil.h @@ -11,7 +11,7 @@ #define LLVM_CLANG_CODEGEN_BACKENDUTIL_H #include "clang/Basic/LLVM.h" -#include "llvm/IR/FunctionInfo.h" +#include "llvm/IR/ModuleSummaryIndex.h" #include namespace llvm { diff --git a/include/clang/Frontend/CodeGenOptions.def b/include/clang/Frontend/CodeGenOptions.def index fa707e38e23..6d2bec547e9 100644 --- a/include/clang/Frontend/CodeGenOptions.def +++ b/include/clang/Frontend/CodeGenOptions.def @@ -74,8 +74,8 @@ CODEGENOPT(LessPreciseFPMAD , 1, 0) ///< Enable less precise MAD instructions t ///< be generated. CODEGENOPT(PrepareForLTO , 1, 0) ///< Set when -flto is enabled on the ///< compile step. -CODEGENOPT(EmitFunctionSummary, 1, 0) ///< Set when -flto=thin is enabled on the - ///< compile step. +CODEGENOPT(EmitSummaryIndex, 1, 0) ///< Set when -flto=thin is enabled on the + ///< compile step. CODEGENOPT(IncrementalLinkerCompatible, 1, 0) ///< Emit an object file which can ///< be used with an incremental ///< linker. diff --git a/lib/CodeGen/BackendUtil.cpp b/lib/CodeGen/BackendUtil.cpp index 843d1b0d371..919bee94ee3 100644 --- a/lib/CodeGen/BackendUtil.cpp +++ b/lib/CodeGen/BackendUtil.cpp @@ -22,13 +22,13 @@ #include "llvm/CodeGen/RegAllocRegistry.h" #include "llvm/CodeGen/SchedulerRegistry.h" #include "llvm/IR/DataLayout.h" -#include "llvm/IR/FunctionInfo.h" +#include "llvm/IR/ModuleSummaryIndex.h" #include "llvm/IR/IRPrintingPasses.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Module.h" #include "llvm/IR/Verifier.h" #include "llvm/MC/SubtargetFeature.h" -#include "llvm/Object/FunctionIndexObjectFile.h" +#include "llvm/Object/ModuleSummaryIndexObjectFile.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/TargetRegistry.h" @@ -97,7 +97,7 @@ class EmitAssemblyHelper { return PerFunctionPasses; } - void CreatePasses(FunctionInfoIndex *FunctionIndex); + void CreatePasses(ModuleSummaryIndex *ModuleSummary); /// Generates the TargetMachine. /// Returns Null if it is unable to create the target machine. @@ -277,7 +277,7 @@ static void addSymbolRewriterPass(const CodeGenOptions &Opts, MPM->add(createRewriteSymbolsPass(DL)); } -void EmitAssemblyHelper::CreatePasses(FunctionInfoIndex *FunctionIndex) { +void EmitAssemblyHelper::CreatePasses(ModuleSummaryIndex *ModuleSummary) { if (CodeGenOpts.DisableLLVMPasses) return; @@ -324,16 +324,16 @@ void EmitAssemblyHelper::CreatePasses(FunctionInfoIndex *FunctionIndex) { PMBuilder.DisableUnitAtATime = !CodeGenOpts.UnitAtATime; PMBuilder.DisableUnrollLoops = !CodeGenOpts.UnrollLoops; PMBuilder.MergeFunctions = CodeGenOpts.MergeFunctions; - PMBuilder.PrepareForThinLTO = CodeGenOpts.EmitFunctionSummary; + PMBuilder.PrepareForThinLTO = CodeGenOpts.EmitSummaryIndex; PMBuilder.PrepareForLTO = CodeGenOpts.PrepareForLTO; PMBuilder.RerollLoops = CodeGenOpts.RerollLoops; legacy::PassManager *MPM = getPerModulePasses(); // If we are performing a ThinLTO importing compile, invoke the LTO - // pipeline and pass down the in-memory function index. - if (FunctionIndex) { - PMBuilder.FunctionIndex = FunctionIndex; + // pipeline and pass down the in-memory module summary index. + if (ModuleSummary) { + PMBuilder.ModuleSummary = ModuleSummary; PMBuilder.populateThinLTOPassManager(*MPM); return; } @@ -645,24 +645,24 @@ void EmitAssemblyHelper::EmitAssembly(BackendAction Action, // If we are performing a ThinLTO importing compile, load the function // index into memory and pass it into CreatePasses, which will add it // to the PassManagerBuilder and invoke LTO passes. - std::unique_ptr FunctionIndex; + std::unique_ptr ModuleSummary; if (!CodeGenOpts.ThinLTOIndexFile.empty()) { - ErrorOr> IndexOrErr = - llvm::getFunctionIndexForFile(CodeGenOpts.ThinLTOIndexFile, - [&](const DiagnosticInfo &DI) { - TheModule->getContext().diagnose(DI); - }); + ErrorOr> IndexOrErr = + llvm::getModuleSummaryIndexForFile( + CodeGenOpts.ThinLTOIndexFile, [&](const DiagnosticInfo &DI) { + TheModule->getContext().diagnose(DI); + }); if (std::error_code EC = IndexOrErr.getError()) { std::string Error = EC.message(); errs() << "Error loading index file '" << CodeGenOpts.ThinLTOIndexFile << "': " << Error << "\n"; return; } - FunctionIndex = std::move(IndexOrErr.get()); - assert(FunctionIndex && "Expected non-empty function index"); + ModuleSummary = std::move(IndexOrErr.get()); + assert(ModuleSummary && "Expected non-empty module summary index"); } - CreatePasses(FunctionIndex.get()); + CreatePasses(ModuleSummary.get()); switch (Action) { case Backend_EmitNothing: @@ -670,7 +670,7 @@ void EmitAssemblyHelper::EmitAssembly(BackendAction Action, case Backend_EmitBC: getPerModulePasses()->add(createBitcodeWriterPass( - *OS, CodeGenOpts.EmitLLVMUseLists, CodeGenOpts.EmitFunctionSummary)); + *OS, CodeGenOpts.EmitLLVMUseLists, CodeGenOpts.EmitSummaryIndex)); break; case Backend_EmitLL: diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 84c3abb51fd..fff211a5845 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -560,7 +560,7 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, Opts.PrepareForLTO = Args.hasArg(OPT_flto, OPT_flto_EQ); const Arg *A = Args.getLastArg(OPT_flto, OPT_flto_EQ); - Opts.EmitFunctionSummary = A && A->containsValue("thin"); + Opts.EmitSummaryIndex = A && A->containsValue("thin"); if (Arg *A = Args.getLastArg(OPT_fthinlto_index_EQ)) { if (IK != IK_LLVM_IR) Diags.Report(diag::err_drv_argument_only_allowed_with) From d644ccf071773d026026046fdc0138a9c73f3dc7 Mon Sep 17 00:00:00 2001 From: Mehdi Amini Date: Sat, 19 Mar 2016 21:49:28 -0700 Subject: [PATCH 301/742] Revert "Fix clang crash: when CodeGenAction is initialized without a context, use the member and not the parameter" This reverts commit c18ed3735dac799123d6d3a121d72cacec46a313. --- lib/CodeGen/CodeGenAction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CodeGen/CodeGenAction.cpp b/lib/CodeGen/CodeGenAction.cpp index cdae0884db8..b8faf8b1735 100644 --- a/lib/CodeGen/CodeGenAction.cpp +++ b/lib/CodeGen/CodeGenAction.cpp @@ -629,7 +629,7 @@ CodeGenAction::CodeGenAction(unsigned _Act, LLVMContext *_VMContext) #ifdef NDEBUG // FIXME: change this to be controlled by a cc1 flag that the driver passes, // on the model of --disable-free - VMContext->setDiscardValueNames(true); + _VMContext->setDiscardValueNames(true); #endif } From 327307fdccf69d24894fc7de05b06b793e0ca920 Mon Sep 17 00:00:00 2001 From: Mehdi Amini Date: Sat, 19 Mar 2016 21:49:29 -0700 Subject: [PATCH 302/742] Revert "Fix build: use -> with pointers and not ." This reverts commit 3baf48c677b8afd92b22cb819cf0283fa8e70cf8. --- lib/CodeGen/CodeGenAction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CodeGen/CodeGenAction.cpp b/lib/CodeGen/CodeGenAction.cpp index b8faf8b1735..fac69ba447e 100644 --- a/lib/CodeGen/CodeGenAction.cpp +++ b/lib/CodeGen/CodeGenAction.cpp @@ -629,7 +629,7 @@ CodeGenAction::CodeGenAction(unsigned _Act, LLVMContext *_VMContext) #ifdef NDEBUG // FIXME: change this to be controlled by a cc1 flag that the driver passes, // on the model of --disable-free - _VMContext->setDiscardValueNames(true); + _VMContext.setDiscardValueNames(true); #endif } From a805071eee5f2533b368ed6dad21377e3fa8bc87 Mon Sep 17 00:00:00 2001 From: Mehdi Amini Date: Sat, 19 Mar 2016 21:49:31 -0700 Subject: [PATCH 303/742] Revert "Remove compile time PreserveName switch based on NDEBUG" This reverts commit 97af164e2d2a0c84eb23cd08d1ebd5e823a2be59. --- lib/CodeGen/CGBuilder.h | 19 +++++++++++++++---- lib/CodeGen/CGCall.cpp | 2 +- lib/CodeGen/CGExpr.cpp | 2 ++ lib/CodeGen/CodeGenAction.cpp | 8 +------- lib/CodeGen/CodeGenFunction.cpp | 20 +++++++++++++++++--- 5 files changed, 36 insertions(+), 15 deletions(-) diff --git a/lib/CodeGen/CGBuilder.h b/lib/CodeGen/CGBuilder.h index d6fc1488677..489f3413d4b 100644 --- a/lib/CodeGen/CGBuilder.h +++ b/lib/CodeGen/CGBuilder.h @@ -22,7 +22,9 @@ class CodeGenFunction; /// \brief This is an IRBuilder insertion helper that forwards to /// CodeGenFunction::InsertHelper, which adds necessary metadata to /// instructions. -class CGBuilderInserter : protected llvm::IRBuilderDefaultInserter { +template +class CGBuilderInserter + : protected llvm::IRBuilderDefaultInserter { public: CGBuilderInserter() = default; explicit CGBuilderInserter(CodeGenFunction *CGF) : CGF(CGF) {} @@ -36,10 +38,17 @@ class CGBuilderInserter : protected llvm::IRBuilderDefaultInserter { CodeGenFunction *CGF = nullptr; }; -typedef CGBuilderInserter CGBuilderInserterTy; +// Don't preserve names on values in an optimized build. +#ifdef NDEBUG +#define PreserveNames false +#else +#define PreserveNames true +#endif + +typedef CGBuilderInserter CGBuilderInserterTy; -typedef llvm::IRBuilder - CGBuilderBaseTy; +typedef llvm::IRBuilder CGBuilderBaseTy; class CGBuilderTy : public CGBuilderBaseTy { /// Storing a reference to the type cache here makes it a lot easier @@ -289,6 +298,8 @@ class CGBuilderTy : public CGBuilderBaseTy { } }; +#undef PreserveNames + } // end namespace CodeGen } // end namespace clang diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index 1fcb934f05e..645dc5ae767 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -3583,7 +3583,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, } llvm::Instruction *CI = CS.getInstruction(); - if (!CI->getType()->isVoidTy()) + if (Builder.isNamePreserving() && !CI->getType()->isVoidTy()) CI->setName("call"); // Emit any writebacks immediately. Arguably this should happen diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index e68f151f8aa..7d1c77a14f9 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/lib/CodeGen/CGExpr.cpp @@ -66,6 +66,8 @@ Address CodeGenFunction::CreateTempAlloca(llvm::Type *Ty, CharUnits Align, /// block. llvm::AllocaInst *CodeGenFunction::CreateTempAlloca(llvm::Type *Ty, const Twine &Name) { + if (!Builder.isNamePreserving()) + return new llvm::AllocaInst(Ty, nullptr, "", AllocaInsertPt); return new llvm::AllocaInst(Ty, nullptr, Name, AllocaInsertPt); } diff --git a/lib/CodeGen/CodeGenAction.cpp b/lib/CodeGen/CodeGenAction.cpp index fac69ba447e..cdd9d09d048 100644 --- a/lib/CodeGen/CodeGenAction.cpp +++ b/lib/CodeGen/CodeGenAction.cpp @@ -625,13 +625,7 @@ void BackendConsumer::DiagnosticHandlerImpl(const DiagnosticInfo &DI) { CodeGenAction::CodeGenAction(unsigned _Act, LLVMContext *_VMContext) : Act(_Act), VMContext(_VMContext ? _VMContext : new LLVMContext), - OwnsVMContext(!_VMContext) { -#ifdef NDEBUG - // FIXME: change this to be controlled by a cc1 flag that the driver passes, - // on the model of --disable-free - _VMContext.setDiscardValueNames(true); -#endif -} + OwnsVMContext(!_VMContext) {} CodeGenAction::~CodeGenAction() { TheModule.reset(); diff --git a/lib/CodeGen/CodeGenFunction.cpp b/lib/CodeGen/CodeGenFunction.cpp index 84edc92107f..e079b5869d9 100644 --- a/lib/CodeGen/CodeGenFunction.cpp +++ b/lib/CodeGen/CodeGenFunction.cpp @@ -745,7 +745,9 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, // later. Don't create this with the builder, because we don't want it // folded. llvm::Value *Undef = llvm::UndefValue::get(Int32Ty); - AllocaInsertPt = new llvm::BitCastInst(Undef, Int32Ty, "allocapt", EntryBB); + AllocaInsertPt = new llvm::BitCastInst(Undef, Int32Ty, "", EntryBB); + if (Builder.isNamePreserving()) + AllocaInsertPt->setName("allocapt"); ReturnBlock = getJumpDestInCurrentScope("return"); @@ -1858,14 +1860,26 @@ void CodeGenFunction::InsertHelper(llvm::Instruction *I, CGM.getSanitizerMetadata()->disableSanitizerForInstruction(I); } -void CGBuilderInserter::InsertHelper( +template +void CGBuilderInserter::InsertHelper( llvm::Instruction *I, const llvm::Twine &Name, llvm::BasicBlock *BB, llvm::BasicBlock::iterator InsertPt) const { - llvm::IRBuilderDefaultInserter::InsertHelper(I, Name, BB, InsertPt); + llvm::IRBuilderDefaultInserter::InsertHelper(I, Name, BB, + InsertPt); if (CGF) CGF->InsertHelper(I, Name, BB, InsertPt); } +#ifdef NDEBUG +#define PreserveNames false +#else +#define PreserveNames true +#endif +template void CGBuilderInserter::InsertHelper( + llvm::Instruction *I, const llvm::Twine &Name, llvm::BasicBlock *BB, + llvm::BasicBlock::iterator InsertPt) const; +#undef PreserveNames + static bool hasRequiredFeatures(const SmallVectorImpl &ReqFeatures, CodeGenModule &CGM, const FunctionDecl *FD, std::string &FirstMissing) { From 79bb77f34786f86f22c75f02a8ad0a44e87d5b90 Mon Sep 17 00:00:00 2001 From: Mehdi Amini Date: Sun, 13 Mar 2016 21:05:23 +0000 Subject: [PATCH 304/742] Remove compile time PreserveName in favor of a runtime cc1 -discard-value-names option Summary: This flag is enabled by default in the driver when NDEBUG is set. It is forwarded on the LLVMContext to discard all value names (but GlobalValue) for performance purpose. This an improved version of D18024 Reviewers: echristo, chandlerc Subscribers: cfe-commits Differential Revision: http://reviews.llvm.org/D18127 From: Mehdi Amini git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263394 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CGBuilder.h | 19 ++++--------------- lib/CodeGen/CGCall.cpp | 2 +- lib/CodeGen/CGExpr.cpp | 2 -- lib/CodeGen/CodeGenFunction.cpp | 20 +++----------------- 4 files changed, 8 insertions(+), 35 deletions(-) diff --git a/lib/CodeGen/CGBuilder.h b/lib/CodeGen/CGBuilder.h index 489f3413d4b..d6fc1488677 100644 --- a/lib/CodeGen/CGBuilder.h +++ b/lib/CodeGen/CGBuilder.h @@ -22,9 +22,7 @@ class CodeGenFunction; /// \brief This is an IRBuilder insertion helper that forwards to /// CodeGenFunction::InsertHelper, which adds necessary metadata to /// instructions. -template -class CGBuilderInserter - : protected llvm::IRBuilderDefaultInserter { +class CGBuilderInserter : protected llvm::IRBuilderDefaultInserter { public: CGBuilderInserter() = default; explicit CGBuilderInserter(CodeGenFunction *CGF) : CGF(CGF) {} @@ -38,17 +36,10 @@ class CGBuilderInserter CodeGenFunction *CGF = nullptr; }; -// Don't preserve names on values in an optimized build. -#ifdef NDEBUG -#define PreserveNames false -#else -#define PreserveNames true -#endif - -typedef CGBuilderInserter CGBuilderInserterTy; +typedef CGBuilderInserter CGBuilderInserterTy; -typedef llvm::IRBuilder CGBuilderBaseTy; +typedef llvm::IRBuilder + CGBuilderBaseTy; class CGBuilderTy : public CGBuilderBaseTy { /// Storing a reference to the type cache here makes it a lot easier @@ -298,8 +289,6 @@ class CGBuilderTy : public CGBuilderBaseTy { } }; -#undef PreserveNames - } // end namespace CodeGen } // end namespace clang diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index 645dc5ae767..1fcb934f05e 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -3583,7 +3583,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, } llvm::Instruction *CI = CS.getInstruction(); - if (Builder.isNamePreserving() && !CI->getType()->isVoidTy()) + if (!CI->getType()->isVoidTy()) CI->setName("call"); // Emit any writebacks immediately. Arguably this should happen diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index 7d1c77a14f9..e68f151f8aa 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/lib/CodeGen/CGExpr.cpp @@ -66,8 +66,6 @@ Address CodeGenFunction::CreateTempAlloca(llvm::Type *Ty, CharUnits Align, /// block. llvm::AllocaInst *CodeGenFunction::CreateTempAlloca(llvm::Type *Ty, const Twine &Name) { - if (!Builder.isNamePreserving()) - return new llvm::AllocaInst(Ty, nullptr, "", AllocaInsertPt); return new llvm::AllocaInst(Ty, nullptr, Name, AllocaInsertPt); } diff --git a/lib/CodeGen/CodeGenFunction.cpp b/lib/CodeGen/CodeGenFunction.cpp index e079b5869d9..84edc92107f 100644 --- a/lib/CodeGen/CodeGenFunction.cpp +++ b/lib/CodeGen/CodeGenFunction.cpp @@ -745,9 +745,7 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, // later. Don't create this with the builder, because we don't want it // folded. llvm::Value *Undef = llvm::UndefValue::get(Int32Ty); - AllocaInsertPt = new llvm::BitCastInst(Undef, Int32Ty, "", EntryBB); - if (Builder.isNamePreserving()) - AllocaInsertPt->setName("allocapt"); + AllocaInsertPt = new llvm::BitCastInst(Undef, Int32Ty, "allocapt", EntryBB); ReturnBlock = getJumpDestInCurrentScope("return"); @@ -1860,26 +1858,14 @@ void CodeGenFunction::InsertHelper(llvm::Instruction *I, CGM.getSanitizerMetadata()->disableSanitizerForInstruction(I); } -template -void CGBuilderInserter::InsertHelper( +void CGBuilderInserter::InsertHelper( llvm::Instruction *I, const llvm::Twine &Name, llvm::BasicBlock *BB, llvm::BasicBlock::iterator InsertPt) const { - llvm::IRBuilderDefaultInserter::InsertHelper(I, Name, BB, - InsertPt); + llvm::IRBuilderDefaultInserter::InsertHelper(I, Name, BB, InsertPt); if (CGF) CGF->InsertHelper(I, Name, BB, InsertPt); } -#ifdef NDEBUG -#define PreserveNames false -#else -#define PreserveNames true -#endif -template void CGBuilderInserter::InsertHelper( - llvm::Instruction *I, const llvm::Twine &Name, llvm::BasicBlock *BB, - llvm::BasicBlock::iterator InsertPt) const; -#undef PreserveNames - static bool hasRequiredFeatures(const SmallVectorImpl &ReqFeatures, CodeGenModule &CGM, const FunctionDecl *FD, std::string &FirstMissing) { From 0963912b6a05dfc944e9afb158803efde86c6f8d Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Sun, 20 Mar 2016 18:24:33 +0000 Subject: [PATCH 305/742] [tsan] Allow -fsanitize=thread for iOS-style simulator targets Update the clang driver to allow -fsanitize=thread when targeting x86_64 iOS and tvOS simulators. Also restrict TSan targeting OS X to only be supported on x86_64 and not i386. Differential Revision: http://reviews.llvm.org/D18280 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263913 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit d56a254584f739e855149bad5d85b87c70a46732) --- lib/Driver/ToolChains.cpp | 7 ++++++- test/Driver/fsanitize.c | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 45cd8bdb29c..001da47ab4b 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -1220,6 +1220,7 @@ void Darwin::CheckObjCARC() const { } SanitizerMask Darwin::getSupportedSanitizers() const { + const bool IsX86_64 = getTriple().getArch() == llvm::Triple::x86_64; SanitizerMask Res = ToolChain::getSupportedSanitizers(); if (isTargetMacOS() || isTargetIOSSimulator()) Res |= SanitizerKind::Address; @@ -1227,7 +1228,11 @@ SanitizerMask Darwin::getSupportedSanitizers() const { if (!isMacosxVersionLT(10, 9)) Res |= SanitizerKind::Vptr; Res |= SanitizerKind::SafeStack; - Res |= SanitizerKind::Thread; + if (IsX86_64) + Res |= SanitizerKind::Thread; + } else if (isTargetIOSSimulator() || isTargetTvOSSimulator()) { + if (IsX86_64) + Res |= SanitizerKind::Thread; } return Res; } diff --git a/test/Driver/fsanitize.c b/test/Driver/fsanitize.c index 09eead21364..2236931ca30 100644 --- a/test/Driver/fsanitize.c +++ b/test/Driver/fsanitize.c @@ -217,6 +217,27 @@ // CHECK-TSAN-MSAN-MSAN-DARWIN: unsupported option '-fsanitize=memory' for target 'x86_64-apple-darwin10' // CHECK-TSAN-MSAN-MSAN-DARWIN-NOT: unsupported option +// RUN: %clang -target x86_64-apple-darwin -fsanitize=thread %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TSAN-X86-64-DARWIN +// CHECK-TSAN-X86-64-DARWIN-NOT: unsupported option + +// RUN: %clang -target x86_64-apple-iossimulator -fsanitize=thread %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TSAN-X86-64-IOSSIMULATOR +// CHECK-TSAN-X86-64-IOSSIMULATOR-NOT: unsupported option + +// RUN: %clang -target x86_64-apple-tvossimulator -fsanitize=thread %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TSAN-X86-64-TVOSSIMULATOR +// CHECK-TSAN-X86-64-TVOSSIMULATOR-NOT: unsupported option + +// RUN: %clang -target i386-apple-darwin -fsanitize=thread %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TSAN-I386-DARWIN +// CHECK-TSAN-I386-DARWIN: unsupported option '-fsanitize=thread' for target 'i386-apple-darwin' + +// RUN: %clang -target arm-apple-ios -fsanitize=thread %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TSAN-ARM-IOS +// CHECK-TSAN-ARM-IOS: unsupported option '-fsanitize=thread' for target 'arm-apple-ios' + +// RUN: %clang -target i386-apple-iossimulator -fsanitize=thread %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TSAN-I386-IOSSIMULATOR +// CHECK-TSAN-I386-IOSSIMULATOR: unsupported option '-fsanitize=thread' for target 'i386-apple-iossimulator' + +// RUN: %clang -target i386-apple-tvossimulator -fsanitize=thread %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TSAN-I386-TVOSSIMULATOR +// CHECK-TSAN-I386-TVOSSIMULATOR: unsupported option '-fsanitize=thread' for target 'i386-apple-tvossimulator' + // RUN: %clang -target x86_64-apple-darwin10 -fsanitize=function %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-FSAN-DARWIN // CHECK-FSAN-DARWIN: unsupported option '-fsanitize=function' for target 'x86_64-apple-darwin10' From b92d64d70350060af2dfe4ff744d97287630ed36 Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Wed, 16 Mar 2016 18:50:49 +0000 Subject: [PATCH 306/742] Add an optional string argument to DeprecatedAttr for Fix-It. We only add this to __attribute__((deprecated)). Differential Revision: http://reviews.llvm.org/D17865 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263652 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/Attr.td | 7 +++- include/clang/Basic/AttrDocs.td | 22 ++++++++++ lib/Lex/PPMacroExpansion.cpp | 1 + lib/Sema/SemaDeclAttr.cpp | 40 +++++++++++++++++-- .../attr-deprecated-replacement-error.cpp | 17 ++++++++ .../attr-deprecated-replacement-fixit.cpp | 16 ++++++++ test/SemaCXX/cxx11-attr-print.cpp | 9 +++++ utils/TableGen/ClangAttrEmitter.cpp | 11 +++++ 8 files changed, 117 insertions(+), 6 deletions(-) create mode 100644 test/SemaCXX/attr-deprecated-replacement-error.cpp create mode 100644 test/SemaCXX/attr-deprecated-replacement-fixit.cpp diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 0426ebfbc59..d67ab1f6fdf 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -704,8 +704,11 @@ def OpenCLGenericAddressSpace : TypeAttr { def Deprecated : InheritableAttr { let Spellings = [GCC<"deprecated">, Declspec<"deprecated">, CXX11<"","deprecated", 201309>]; - let Args = [StringArgument<"Message", 1>]; - let Documentation = [Undocumented]; + let Args = [StringArgument<"Message", 1>, + // An optional string argument that enables us to provide a + // Fix-It. + StringArgument<"Replacement", 1>]; + let Documentation = [DeprecatedDocs]; } def Destructor : InheritableAttr { diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td index 676b0720a35..8a9edbea736 100644 --- a/include/clang/Basic/AttrDocs.td +++ b/include/clang/Basic/AttrDocs.td @@ -1960,3 +1960,25 @@ hardware design, touch the red zone. The system will crash if the wrong handler is used. }]; } + +def DeprecatedDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``deprecated`` attribute can be applied to a function, a variable, or a +type. This is useful when identifying functions, variables, or types that are +expected to be removed in a future version of a program. + +Consider the function declaration for a hypothetical function ``f``: + +.. code-block:: c++ + + void f(void) __attribute__((deprecated("message", "replacement"))); + +When spelled as `__attribute__((deprecated))`, the deprecated attribute can have +two optional string arguments. The first one is the message to display when +emitting the warning; the second one enables the compiler to provide a Fix-It +to replace the deprecated name with a new name. Otherwise, when spelled as +`[[gnu::deprecated]] or [[deprecated]]`, the attribute can have one optional +string argument which is the message to display when emitting the warning. + }]; +} diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp index a25694930b1..dbed764a714 100644 --- a/lib/Lex/PPMacroExpansion.cpp +++ b/lib/Lex/PPMacroExpansion.cpp @@ -1076,6 +1076,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .Case("attribute_cf_returns_retained", true) .Case("attribute_cf_returns_on_parameters", true) .Case("attribute_deprecated_with_message", true) + .Case("attribute_deprecated_with_replacement", true) .Case("attribute_ext_vector_type", true) .Case("attribute_ns_returns_not_retained", true) .Case("attribute_ns_returns_retained", true) diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 29e828f6bb9..83b507d5e6c 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -5274,12 +5274,27 @@ static void handleDeprecatedAttr(Sema &S, Decl *D, const AttributeList &Attr) { } } + // Handle the cases where the attribute has a text message. + StringRef Str, Replacement; + if (Attr.isArgExpr(0) && Attr.getArgAsExpr(0) && + !S.checkStringLiteralArgumentAttr(Attr, 0, Str)) + return; + + // Only support a single optional message for Declspec and CXX11. + if (Attr.isDeclspecAttribute() || Attr.isCXX11Attribute()) + checkAttributeAtMostNumArgs(S, Attr, 1); + else if (Attr.isArgExpr(1) && Attr.getArgAsExpr(1) && + !S.checkStringLiteralArgumentAttr(Attr, 1, Replacement)) + return; + if (!S.getLangOpts().CPlusPlus14) if (Attr.isCXX11Attribute() && !(Attr.hasScope() && Attr.getScopeName()->isStr("gnu"))) S.Diag(Attr.getLoc(), diag::ext_deprecated_attr_is_a_cxx14_extension); - handleAttrWithMessage(S, D, Attr); + D->addAttr(::new (S.Context) DeprecatedAttr(Attr.getRange(), S.Context, Str, + Replacement, + Attr.getAttributeSpellingListIndex())); } static void handleNoSanitizeAttr(Sema &S, Decl *D, const AttributeList &Attr) { @@ -6339,18 +6354,35 @@ static void DoEmitAvailabilityWarning(Sema &S, Sema::AvailabilityDiagnostic K, break; } + CharSourceRange UseRange; + StringRef Replacement; + if (K == Sema::AD_Deprecation) { + if (auto attr = D->getAttr()) + Replacement = attr->getReplacement(); + + if (!Replacement.empty()) + UseRange = + CharSourceRange::getCharRange(Loc, S.getLocForEndOfToken(Loc)); + } + if (!Message.empty()) { - S.Diag(Loc, diag_message) << D << Message; + S.Diag(Loc, diag_message) << D << Message + << (UseRange.isValid() ? + FixItHint::CreateReplacement(UseRange, Replacement) : FixItHint()); if (ObjCProperty) S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute) << ObjCProperty->getDeclName() << property_note_select; } else if (!UnknownObjCClass) { - S.Diag(Loc, diag) << D; + S.Diag(Loc, diag) << D + << (UseRange.isValid() ? + FixItHint::CreateReplacement(UseRange, Replacement) : FixItHint()); if (ObjCProperty) S.Diag(ObjCProperty->getLocation(), diag::note_property_attribute) << ObjCProperty->getDeclName() << property_note_select; } else { - S.Diag(Loc, diag_fwdclass_message) << D; + S.Diag(Loc, diag_fwdclass_message) << D + << (UseRange.isValid() ? + FixItHint::CreateReplacement(UseRange, Replacement) : FixItHint()); S.Diag(UnknownObjCClass->getLocation(), diag::note_forward_class); } diff --git a/test/SemaCXX/attr-deprecated-replacement-error.cpp b/test/SemaCXX/attr-deprecated-replacement-error.cpp new file mode 100644 index 00000000000..54d0f9e74f3 --- /dev/null +++ b/test/SemaCXX/attr-deprecated-replacement-error.cpp @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -verify -fsyntax-only -std=c++11 -fms-extensions %s + +#if !__has_feature(attribute_deprecated_with_replacement) +#error "Missing __has_feature" +#endif + +int a1 [[deprecated("warning", "fixit")]]; // expected-error{{'deprecated' attribute takes no more than 1 argument}} +int a2 [[deprecated("warning", 1)]]; // expected-error{{'deprecated' attribute takes no more than 1 argument}} + +int b1 [[gnu::deprecated("warning", "fixit")]]; // expected-error{{'deprecated' attribute takes no more than 1 argument}} +int b2 [[gnu::deprecated("warning", 1)]]; // expected-error{{'deprecated' attribute takes no more than 1 argument}} + +__declspec(deprecated("warning", "fixit")) int c1; // expected-error{{'deprecated' attribute takes no more than 1 argument}} +__declspec(deprecated("warning", 1)) int c2; // expected-error{{'deprecated' attribute takes no more than 1 argument}} + +int d1 __attribute__((deprecated("warning", "fixit"))); +int d2 __attribute__((deprecated("warning", 1))); // expected-error{{'deprecated' attribute requires a string}} diff --git a/test/SemaCXX/attr-deprecated-replacement-fixit.cpp b/test/SemaCXX/attr-deprecated-replacement-fixit.cpp new file mode 100644 index 00000000000..9a2d0f409aa --- /dev/null +++ b/test/SemaCXX/attr-deprecated-replacement-fixit.cpp @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -verify -fsyntax-only %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s +// RUN: cp %s %t +// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fixit %t +// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -Werror %t + +#if !__has_feature(attribute_deprecated_with_replacement) +#error "Missing __has_feature" +#endif + +void f_8(int) __attribute__((deprecated("message", "new8"))); // expected-note {{'f_8' has been explicitly marked deprecated here}} +void new8(int); +void test() { + f_8(0); // expected-warning{{'f_8' is deprecated}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:6}:"new8" +} diff --git a/test/SemaCXX/cxx11-attr-print.cpp b/test/SemaCXX/cxx11-attr-print.cpp index 999eaed6180..12b41758c74 100644 --- a/test/SemaCXX/cxx11-attr-print.cpp +++ b/test/SemaCXX/cxx11-attr-print.cpp @@ -16,6 +16,15 @@ int a __attribute__((deprecated("warning"))); // CHECK: int b {{\[}}[gnu::deprecated("warning")]]; int b [[gnu::deprecated("warning")]]; +// CHECK: __declspec(deprecated("warning")) +__declspec(deprecated("warning")) int c; + +// CHECK: int d {{\[}}[deprecated("warning")]]; +int d [[deprecated("warning")]]; + +// CHECK: __attribute__((deprecated("warning", "fixit"))); +int e __attribute__((deprecated("warning", "fixit"))); + // CHECK: int cxx11_alignas alignas(4); alignas(4) int cxx11_alignas; diff --git a/utils/TableGen/ClangAttrEmitter.cpp b/utils/TableGen/ClangAttrEmitter.cpp index c0574449acc..a77f80c6d24 100644 --- a/utils/TableGen/ClangAttrEmitter.cpp +++ b/utils/TableGen/ClangAttrEmitter.cpp @@ -1097,6 +1097,15 @@ static void writeAvailabilityValue(raw_ostream &OS) { << " OS << \""; } +static void writeDeprecatedAttrValue(raw_ostream &OS, std::string &Variety) { + OS << "\\\"\" << getMessage() << \"\\\"\";\n"; + // Only GNU deprecated has an optional fixit argument at the second position. + if (Variety == "GNU") + OS << " if (!getReplacement().empty()) OS << \", \\\"\"" + " << getReplacement() << \"\\\"\";\n"; + OS << " OS << \""; +} + static void writeGetSpellingFunction(Record &R, raw_ostream &OS) { std::vector Spellings = GetFlattenedSpellings(R); @@ -1210,6 +1219,8 @@ writePrettyPrintFunction(Record &R, OS << "("; if (Spelling == "availability") { writeAvailabilityValue(OS); + } else if (Spelling == "deprecated" || Spelling == "gnu::deprecated") { + writeDeprecatedAttrValue(OS, Variety); } else { unsigned index = 0; for (const auto &arg : Args) { From d131377b3f9adccd09b49d9c6c6e6906689ae381 Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Mon, 21 Mar 2016 12:15:27 -0700 Subject: [PATCH 307/742] Update testing case on swift-3.0 branch. --- test/SemaCXX/attr-deprecated-replacement-error.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/SemaCXX/attr-deprecated-replacement-error.cpp b/test/SemaCXX/attr-deprecated-replacement-error.cpp index 54d0f9e74f3..2bbefe1455f 100644 --- a/test/SemaCXX/attr-deprecated-replacement-error.cpp +++ b/test/SemaCXX/attr-deprecated-replacement-error.cpp @@ -4,8 +4,8 @@ #error "Missing __has_feature" #endif -int a1 [[deprecated("warning", "fixit")]]; // expected-error{{'deprecated' attribute takes no more than 1 argument}} -int a2 [[deprecated("warning", 1)]]; // expected-error{{'deprecated' attribute takes no more than 1 argument}} +int a1 [[deprecated("warning", "fixit")]]; // expected-warning{{use of the 'deprecated' attribute is a C++14 extension}} expected-error{{'deprecated' attribute takes no more than 1 argument}} +int a2 [[deprecated("warning", 1)]]; // expected-warning{{use of the 'deprecated' attribute is a C++14 extension}} expected-error{{'deprecated' attribute takes no more than 1 argument}} int b1 [[gnu::deprecated("warning", "fixit")]]; // expected-error{{'deprecated' attribute takes no more than 1 argument}} int b2 [[gnu::deprecated("warning", 1)]]; // expected-error{{'deprecated' attribute takes no more than 1 argument}} From 81824a5c90f42ff210ea5a28bd852bd38206e95a Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Mon, 21 Mar 2016 17:26:40 +0000 Subject: [PATCH 308/742] NFC: wrap Availability-related data in its own struct in AttributeList. This makes it easy to add more data into Availability. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263955 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Sema/AttributeList.h | 63 +++++++++++++++--------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/include/clang/Sema/AttributeList.h b/include/clang/Sema/AttributeList.h index 26527036404..56ed1ba5b74 100644 --- a/include/clang/Sema/AttributeList.h +++ b/include/clang/Sema/AttributeList.h @@ -46,6 +46,27 @@ struct AvailabilityChange { bool isValid() const { return !Version.empty(); } }; +namespace { +enum AvailabilitySlot { + IntroducedSlot, DeprecatedSlot, ObsoletedSlot, NumAvailabilitySlots +}; + +/// Describes the trailing object for Availability attribute in AttributeList. +struct AvailabilityData { + AvailabilityChange Changes[NumAvailabilitySlots]; + SourceLocation StrictLoc; + AvailabilityData(const AvailabilityChange &Introduced, + const AvailabilityChange &Deprecated, + const AvailabilityChange &Obsoleted, + SourceLocation Strict) + : StrictLoc(Strict) { + Changes[IntroducedSlot] = Introduced; + Changes[DeprecatedSlot] = Deprecated; + Changes[ObsoletedSlot] = Obsoleted; + } +}; +} + /// \brief Wraps an identifier and optional source location for the identifier. struct IdentifierLoc { SourceLocation Loc; @@ -142,30 +163,13 @@ class AttributeList { // TODO: This should really be called ParsedAttribute return reinterpret_cast(this + 1); } - enum AvailabilitySlot { - IntroducedSlot, DeprecatedSlot, ObsoletedSlot - }; - /// Availability information is stored immediately following the arguments, /// if any, at the end of the object. - AvailabilityChange &getAvailabilitySlot(AvailabilitySlot index) { - return reinterpret_cast(getArgsBuffer() - + NumArgs)[index]; - } - const AvailabilityChange &getAvailabilitySlot(AvailabilitySlot index) const { - return reinterpret_cast(getArgsBuffer() - + NumArgs)[index]; + AvailabilityData *getAvailabilityData() { + return reinterpret_cast(getArgsBuffer() + NumArgs); } - - /// The location of the 'strict' keyword in an availability attribute. - SourceLocation *getStrictSlot() { - return reinterpret_cast( - &getAvailabilitySlot(ObsoletedSlot) + 1); - } - - SourceLocation const *getStrictSlot() const { - return reinterpret_cast( - &getAvailabilitySlot(ObsoletedSlot) + 1); + const AvailabilityData *getAvailabilityData() const { + return reinterpret_cast(getArgsBuffer() + NumArgs); } public: @@ -253,10 +257,8 @@ class AttributeList { // TODO: This should really be called ParsedAttribute NextInPosition(nullptr), NextInPool(nullptr) { ArgsUnion PVal(Parm); memcpy(getArgsBuffer(), &PVal, sizeof(ArgsUnion)); - new (&getAvailabilitySlot(IntroducedSlot)) AvailabilityChange(introduced); - new (&getAvailabilitySlot(DeprecatedSlot)) AvailabilityChange(deprecated); - new (&getAvailabilitySlot(ObsoletedSlot)) AvailabilityChange(obsoleted); - memcpy(getStrictSlot(), &strict, sizeof(SourceLocation)); + new (getAvailabilityData()) AvailabilityData( + introduced, deprecated, obsoleted, strict); AttrKind = getKind(getName(), getScopeName(), syntaxUsed); } @@ -411,22 +413,22 @@ class AttributeList { // TODO: This should really be called ParsedAttribute const AvailabilityChange &getAvailabilityIntroduced() const { assert(getKind() == AT_Availability && "Not an availability attribute"); - return getAvailabilitySlot(IntroducedSlot); + return getAvailabilityData()->Changes[IntroducedSlot]; } const AvailabilityChange &getAvailabilityDeprecated() const { assert(getKind() == AT_Availability && "Not an availability attribute"); - return getAvailabilitySlot(DeprecatedSlot); + return getAvailabilityData()->Changes[DeprecatedSlot]; } const AvailabilityChange &getAvailabilityObsoleted() const { assert(getKind() == AT_Availability && "Not an availability attribute"); - return getAvailabilitySlot(ObsoletedSlot); + return getAvailabilityData()->Changes[ObsoletedSlot]; } SourceLocation getStrictLoc() const { assert(getKind() == AT_Availability && "Not an availability attribute"); - return *getStrictSlot(); + return getAvailabilityData()->StrictLoc; } SourceLocation getUnavailableLoc() const { @@ -504,8 +506,7 @@ class AttributeFactory { /// which we want to ensure is a multiple of sizeof(void*). AvailabilityAllocSize = sizeof(AttributeList) - + ((3 * sizeof(AvailabilityChange) + sizeof(void*) + - sizeof(ArgsUnion) + sizeof(SourceLocation) - 1) + + ((sizeof(AvailabilityData) + sizeof(void*) + sizeof(ArgsUnion) - 1) / sizeof(void*) * sizeof(void*)), TypeTagForDatatypeAllocSize = sizeof(AttributeList) From 3b92bde41613361a7c98fdab32db242008968e82 Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Mon, 21 Mar 2016 17:30:55 +0000 Subject: [PATCH 309/742] Add replacement = "xxx" to AvailabilityAttr. This commit adds a named argument to AvailabilityAttr, while r263652 adds an optional string argument to __attribute__((deprecated)). This was commited in r263687 and reverted in 263752 due to misaligned access. rdar://20588929 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263958 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/Attr.td | 2 +- include/clang/Basic/AttrDocs.td | 5 ++++ include/clang/Parse/Parser.h | 3 +++ include/clang/Sema/AttributeList.h | 23 +++++++++++------- include/clang/Sema/Sema.h | 2 +- lib/Lex/PPMacroExpansion.cpp | 1 + lib/Parse/ParseDecl.cpp | 24 +++++++++++++------ lib/Parse/Parser.cpp | 1 + lib/Sema/SemaDecl.cpp | 3 ++- lib/Sema/SemaDeclAttr.cpp | 14 +++++++++-- .../attr-deprecated-replacement-fixit.cpp | 8 +++++++ 11 files changed, 66 insertions(+), 20 deletions(-) diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index d67ab1f6fdf..a3640876fdb 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -454,7 +454,7 @@ def Availability : InheritableAttr { let Args = [IdentifierArgument<"platform">, VersionArgument<"introduced">, VersionArgument<"deprecated">, VersionArgument<"obsoleted">, BoolArgument<"unavailable">, StringArgument<"message">, - BoolArgument<"strict">]; + BoolArgument<"strict">, StringArgument<"replacement">]; let AdditionalMembers = [{static llvm::StringRef getPrettyPlatformName(llvm::StringRef Platform) { return llvm::StringSwitch(Platform) diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td index 8a9edbea736..568cc83af57 100644 --- a/include/clang/Basic/AttrDocs.td +++ b/include/clang/Basic/AttrDocs.td @@ -661,6 +661,11 @@ message=\ *string-literal* error about use of a deprecated or obsoleted declaration. Useful to direct users to replacement APIs. +replacement=\ *string-literal* + Additional message text that Clang will use to provide Fix-It when emitting + a warning about use of a deprecated declaration. The Fix-It will replace + the deprecated declaration with the new declaration specified. + Multiple availability attributes can be placed on a declaration, which may correspond to different platforms. Only the availability attribute with the platform corresponding to the target platform will be used; any others will be diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 6ca6af91dbf..abacc4aadaa 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -137,6 +137,9 @@ class Parser : public CodeCompletionHandler { /// \brief Identifier for "strict". IdentifierInfo *Ident_strict; + /// \brief Identifier for "replacement". + IdentifierInfo *Ident_replacement; + /// C++0x contextual keywords. mutable IdentifierInfo *Ident_final; mutable IdentifierInfo *Ident_override; diff --git a/include/clang/Sema/AttributeList.h b/include/clang/Sema/AttributeList.h index 56ed1ba5b74..bf20271c877 100644 --- a/include/clang/Sema/AttributeList.h +++ b/include/clang/Sema/AttributeList.h @@ -55,11 +55,12 @@ enum AvailabilitySlot { struct AvailabilityData { AvailabilityChange Changes[NumAvailabilitySlots]; SourceLocation StrictLoc; + const Expr *Replacement; AvailabilityData(const AvailabilityChange &Introduced, const AvailabilityChange &Deprecated, const AvailabilityChange &Obsoleted, - SourceLocation Strict) - : StrictLoc(Strict) { + SourceLocation Strict, const Expr *ReplaceExpr) + : StrictLoc(Strict), Replacement(ReplaceExpr) { Changes[IntroducedSlot] = Introduced; Changes[DeprecatedSlot] = Deprecated; Changes[ObsoletedSlot] = Obsoleted; @@ -248,7 +249,8 @@ class AttributeList { // TODO: This should really be called ParsedAttribute const AvailabilityChange &obsoleted, SourceLocation unavailable, const Expr *messageExpr, - Syntax syntaxUsed, SourceLocation strict) + Syntax syntaxUsed, SourceLocation strict, + const Expr *replacementExpr) : AttrName(attrName), ScopeName(scopeName), AttrRange(attrRange), ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(1), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(true), @@ -258,7 +260,7 @@ class AttributeList { // TODO: This should really be called ParsedAttribute ArgsUnion PVal(Parm); memcpy(getArgsBuffer(), &PVal, sizeof(ArgsUnion)); new (getAvailabilityData()) AvailabilityData( - introduced, deprecated, obsoleted, strict); + introduced, deprecated, obsoleted, strict, replacementExpr); AttrKind = getKind(getName(), getScopeName(), syntaxUsed); } @@ -441,6 +443,11 @@ class AttributeList { // TODO: This should really be called ParsedAttribute return MessageExpr; } + const Expr *getReplacementExpr() const { + assert(getKind() == AT_Availability && "Not an availability attribute"); + return getAvailabilityData()->Replacement; + } + const ParsedType &getMatchingCType() const { assert(getKind() == AT_TypeTagForDatatype && "Not a type_tag_for_datatype attribute"); @@ -625,13 +632,13 @@ class AttributePool { SourceLocation unavailable, const Expr *MessageExpr, AttributeList::Syntax syntax, - SourceLocation strict) { + SourceLocation strict, const Expr *ReplacementExpr) { void *memory = allocate(AttributeFactory::AvailabilityAllocSize); return add(new (memory) AttributeList(attrName, attrRange, scopeName, scopeLoc, Param, introduced, deprecated, obsoleted, unavailable, MessageExpr, - syntax, strict)); + syntax, strict, ReplacementExpr)); } AttributeList *create(IdentifierInfo *attrName, SourceRange attrRange, @@ -761,11 +768,11 @@ class ParsedAttributes { SourceLocation unavailable, const Expr *MessageExpr, AttributeList::Syntax syntax, - SourceLocation strict) { + SourceLocation strict, const Expr *ReplacementExpr) { AttributeList *attr = pool.create(attrName, attrRange, scopeName, scopeLoc, Param, introduced, deprecated, obsoleted, unavailable, MessageExpr, syntax, - strict); + strict, ReplacementExpr); add(attr); return attr; } diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index e7a3cc3ef49..c4025381c08 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -2110,7 +2110,7 @@ class Sema { VersionTuple Obsoleted, bool IsUnavailable, StringRef Message, - bool IsStrict, + bool IsStrict, StringRef Replacement, AvailabilityMergeKind AMK, unsigned AttrSpellingListIndex); TypeVisibilityAttr *mergeTypeVisibilityAttr(Decl *D, SourceRange Range, diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp index dbed764a714..f1e230cb683 100644 --- a/lib/Lex/PPMacroExpansion.cpp +++ b/lib/Lex/PPMacroExpansion.cpp @@ -1071,6 +1071,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .Case("attribute_availability_watchos", true) .Case("attribute_availability_swift", true) .Case("attribute_availability_with_strict", true) + .Case("attribute_availability_with_replacement", true) .Case("attribute_availability_in_templates", true) .Case("attribute_cf_returns_not_retained", true) .Case("attribute_cf_returns_retained", true) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index f306294434e..5e31e0310a4 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -833,7 +833,8 @@ VersionTuple Parser::ParseVersionTuple(SourceRange &Range) { /// \brief Parse the contents of the "availability" attribute. /// /// availability-attribute: -/// 'availability' '(' platform ',' opt-strict version-arg-list, opt-message')' +/// 'availability' '(' platform ',' opt-strict version-arg-list, +/// opt-replacement, opt-message')' /// /// platform: /// identifier @@ -850,6 +851,8 @@ VersionTuple Parser::ParseVersionTuple(SourceRange &Range) { /// 'deprecated' '=' version /// 'obsoleted' = version /// 'unavailable' +/// opt-replacement: +/// 'replacement' '=' /// opt-message: /// 'message' '=' void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability, @@ -861,7 +864,7 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability, AttributeList::Syntax Syntax) { enum { Introduced, Deprecated, Obsoleted, Unknown }; AvailabilityChange Changes[Unknown]; - ExprResult MessageExpr; + ExprResult MessageExpr, ReplacementExpr; // Opening '('. BalancedDelimiterTracker T(*this, tok::l_paren); @@ -893,9 +896,10 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability, Ident_unavailable = PP.getIdentifierInfo("unavailable"); Ident_message = PP.getIdentifierInfo("message"); Ident_strict = PP.getIdentifierInfo("strict"); + Ident_replacement = PP.getIdentifierInfo("replacement"); } - // Parse the optional "strict" and the set of + // Parse the optional "strict", the optional "replacement" and the set of // introductions/deprecations/removals. SourceLocation UnavailableLoc, StrictLoc; do { @@ -931,14 +935,17 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability, return; } ConsumeToken(); - if (Keyword == Ident_message) { + if (Keyword == Ident_message || Keyword == Ident_replacement) { if (Tok.isNot(tok::string_literal)) { Diag(Tok, diag::err_expected_string_literal) << /*Source='availability attribute'*/2; SkipUntil(tok::r_paren, StopAtSemi); return; } - MessageExpr = ParseStringLiteralExpression(); + if (Keyword == Ident_message) + MessageExpr = ParseStringLiteralExpression(); + else + ReplacementExpr = ParseStringLiteralExpression(); // Also reject wide string literals. if (StringLiteral *MessageStringLiteral = cast_or_null(MessageExpr.get())) { @@ -950,7 +957,10 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability, return; } } - break; + if (Keyword == Ident_message) + break; + else + continue; } // Special handling of 'NA' only when applied to introduced or @@ -1037,7 +1047,7 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability, Changes[Deprecated], Changes[Obsoleted], UnavailableLoc, MessageExpr.get(), - Syntax, StrictLoc); + Syntax, StrictLoc, ReplacementExpr.get()); } /// \brief Parse the contents of the "objc_bridge_related" attribute. diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index ac4e52b3acb..c2c7330fedc 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -492,6 +492,7 @@ void Parser::Initialize() { Ident_obsoleted = nullptr; Ident_unavailable = nullptr; Ident_strict = nullptr; + Ident_replacement = nullptr; Ident__except = nullptr; diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index f48ce082fa2..ac517694b86 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -2196,7 +2196,8 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D, NewAttr = S.mergeAvailabilityAttr(D, AA->getRange(), AA->getPlatform(), AA->getIntroduced(), AA->getDeprecated(), AA->getObsoleted(), AA->getUnavailable(), - AA->getMessage(), AA->getStrict(), AMK, + AA->getMessage(), AA->getStrict(), + AA->getReplacement(), AMK, AttrSpellingListIndex); else if (const auto *VA = dyn_cast(Attr)) NewAttr = S.mergeVisibilityAttr(D, VA->getRange(), VA->getVisibility(), diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 83b507d5e6c..c079984c2ce 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -1936,6 +1936,7 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr(NamedDecl *D, SourceRange Range, bool IsUnavailable, StringRef Message, bool IsStrict, + StringRef Replacement, AvailabilityMergeKind AMK, unsigned AttrSpellingListIndex) { VersionTuple MergedIntroduced = Introduced; @@ -2082,7 +2083,8 @@ AvailabilityAttr *Sema::mergeAvailabilityAttr(NamedDecl *D, SourceRange Range, return ::new (Context) AvailabilityAttr(Range, Context, Platform, Introduced, Deprecated, Obsoleted, IsUnavailable, Message, - IsStrict, AttrSpellingListIndex); + IsStrict, Replacement, + AttrSpellingListIndex); } return nullptr; } @@ -2114,6 +2116,10 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, if (const StringLiteral *SE = dyn_cast_or_null(Attr.getMessageExpr())) Str = SE->getString(); + StringRef Replacement; + if (const StringLiteral *SE = + dyn_cast_or_null(Attr.getReplacementExpr())) + Replacement = SE->getString(); if (II->getName() == "swift") { if (Introduced.isValid() || Deprecated.isValid() || Obsoleted.isValid() || @@ -2128,7 +2134,7 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, Deprecated.Version, Obsoleted.Version, IsUnavailable, Str, - IsStrict, + IsStrict, Replacement, Sema::AMK_None, Index); if (NewAttr) @@ -2174,6 +2180,7 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, NewObsoleted, IsUnavailable, Str, IsStrict, + Replacement, Sema::AMK_None, Index); if (NewAttr) @@ -2197,6 +2204,7 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, Obsoleted.Version, IsUnavailable, Str, IsStrict, + Replacement, Sema::AMK_None, Index); if (NewAttr) @@ -6359,6 +6367,8 @@ static void DoEmitAvailabilityWarning(Sema &S, Sema::AvailabilityDiagnostic K, if (K == Sema::AD_Deprecation) { if (auto attr = D->getAttr()) Replacement = attr->getReplacement(); + if (auto attr = D->getAttr()) + Replacement = attr->getReplacement(); if (!Replacement.empty()) UseRange = diff --git a/test/SemaCXX/attr-deprecated-replacement-fixit.cpp b/test/SemaCXX/attr-deprecated-replacement-fixit.cpp index 9a2d0f409aa..8e0af7a648d 100644 --- a/test/SemaCXX/attr-deprecated-replacement-fixit.cpp +++ b/test/SemaCXX/attr-deprecated-replacement-fixit.cpp @@ -8,9 +8,17 @@ #error "Missing __has_feature" #endif +#if !__has_feature(attribute_availability_with_replacement) +#error "Missing __has_feature" +#endif + void f_8(int) __attribute__((deprecated("message", "new8"))); // expected-note {{'f_8' has been explicitly marked deprecated here}} void new8(int); +void f_2(int) __attribute__((availability(macosx,deprecated=9.0,replacement="new2"))); // expected-note {{'f_2' has been explicitly marked deprecated here}} +void new2(int); void test() { f_8(0); // expected-warning{{'f_8' is deprecated}} // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:6}:"new8" + f_2(0); // expected-warning{{'f_2' is deprecated: first deprecated in OS X 9.0}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:6}:"new2" } From ad60588eaf0095a52590ba2514e10cd40de62def Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Mon, 21 Mar 2016 11:44:28 -0700 Subject: [PATCH 310/742] Fix build error with creating 'AvailabilityAttr', after r263958. --- lib/Sema/SemaAPINotes.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Sema/SemaAPINotes.cpp b/lib/Sema/SemaAPINotes.cpp index b9dcfb87341..6e06e4cc80a 100644 --- a/lib/Sema/SemaAPINotes.cpp +++ b/lib/Sema/SemaAPINotes.cpp @@ -118,7 +118,8 @@ static void ProcessAPINotes(Sema &S, Decl *D, VersionTuple(), /*Unavailable=*/true, CopyString(S.Context, Info.UnavailableMsg), - /*Nopartial=*/true)); + /*Strict=*/false, + /*Replacement=*/StringRef())); } // swift_name From 353cb5e27288ee70739e5fa286e2204440f0efc9 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Tue, 22 Mar 2016 05:00:21 +0000 Subject: [PATCH 311/742] [Objective-c] Do not set IsExact to true when the receiver is a class. IsExact shouldn't be set to true in WeakObjectProfileTy::getBaseInfo when the receiver is a class because having a class as the receiver doesn't guarantee that the Base is exact. This is a follow-up to r263818. rdar://problem/25208167 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@264025 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 5dfb2d36c3180d3d3953022e3bbaf01db0012f3c) --- include/clang/Sema/ScopeInfo.h | 1 + lib/Sema/ScopeInfo.cpp | 4 +--- test/SemaObjC/arc-repeated-weak.mm | 11 +++++++++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/include/clang/Sema/ScopeInfo.h b/include/clang/Sema/ScopeInfo.h index d13667e8007..ca740aec62c 100644 --- a/include/clang/Sema/ScopeInfo.h +++ b/include/clang/Sema/ScopeInfo.h @@ -183,6 +183,7 @@ class FunctionScopeInfo { /// [self foo].prop | 0 (unknown) | prop (ObjCPropertyDecl) /// self.prop1.prop2 | prop1 (ObjCPropertyDecl) | prop2 (ObjCPropertyDecl) /// MyClass.prop | MyClass (ObjCInterfaceDecl) | -prop (ObjCMethodDecl) + /// MyClass.foo.prop | +foo (ObjCMethodDecl) | -prop (ObjCPropertyDecl) /// weakVar | 0 (known) | weakVar (VarDecl) /// self->weakIvar | self (VarDecl) | weakIvar (ObjCIvarDecl) /// diff --git a/lib/Sema/ScopeInfo.cpp b/lib/Sema/ScopeInfo.cpp index 144b3a5c86f..8c2c502f250 100644 --- a/lib/Sema/ScopeInfo.cpp +++ b/lib/Sema/ScopeInfo.cpp @@ -85,9 +85,7 @@ FunctionScopeInfo::WeakObjectProfileTy::getBaseInfo(const Expr *E) { if (BaseProp) { D = getBestPropertyDecl(BaseProp); - if (BaseProp->isClassReceiver()) - IsExact = true; - else { + if (BaseProp->isObjectReceiver()) { const Expr *DoubleBase = BaseProp->getBase(); if (const OpaqueValueExpr *OVE = dyn_cast(DoubleBase)) DoubleBase = OVE->getSourceExpr(); diff --git a/test/SemaObjC/arc-repeated-weak.mm b/test/SemaObjC/arc-repeated-weak.mm index 7ac2313fa31..11161a0bf7f 100644 --- a/test/SemaObjC/arc-repeated-weak.mm +++ b/test/SemaObjC/arc-repeated-weak.mm @@ -445,9 +445,20 @@ - (void) Meth : (id) data @class NSString; @interface NSBundle +(NSBundle *)foo; +@property (class) NSBundle *foo2; @property NSString *prop; +@property(weak) NSString *weakProp; +@end + +@interface NSBundle2 : NSBundle @end void foo() { NSString * t = NSBundle.foo.prop; + use(NSBundle.foo.weakProp); // expected-warning{{weak property 'weakProp' may be accessed multiple times}} + use(NSBundle2.foo.weakProp); // expected-note{{also accessed here}} + + NSString * t2 = NSBundle.foo2.prop; + use(NSBundle.foo2.weakProp); // expected-warning{{weak property 'weakProp' may be accessed multiple times}} + use(NSBundle2.foo2.weakProp); // expected-note{{also accessed here}} } From a5ac3891520dde3120f2782db1429508ecac66a0 Mon Sep 17 00:00:00 2001 From: Benjamin Kramer Date: Mon, 14 Mar 2016 13:23:58 +0000 Subject: [PATCH 312/742] [Frontend] Disable value name discarding for all sanitizers. ASan also relies on names on allocas and will emit unhelpful output if they're not present. Just force-enable value names for now. Should unbreak release builds of asan. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263429 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Frontend/CompilerInvocation.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index fff211a5845..42d98076b9d 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -2107,6 +2107,12 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, Success = false; } } + + // FIXME: Override value name discarding when sanitizers are used because the + // backend passes depend on the name of the alloca in order to print out + // names. + Res.getCodeGenOpts().DiscardValueNames &= Res.getLangOpts()->Sanitize.empty(); + // FIXME: ParsePreprocessorArgs uses the FileManager to read the contents of // PCH file and find the original header name. Remove the need to do that in // ParsePreprocessorArgs and remove the FileManager From 4debe93f0a4188f64a841a40d1744d423b3d445f Mon Sep 17 00:00:00 2001 From: Benjamin Kramer Date: Tue, 15 Mar 2016 09:41:39 +0000 Subject: [PATCH 313/742] Restrict the hack from r263429 to asan and msan. The other sanitizers don't have backend passes that rely on value names. Avoid paying the compile time cost of names there. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263541 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Frontend/CompilerInvocation.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 42d98076b9d..013ab461325 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -2108,10 +2108,12 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, } } - // FIXME: Override value name discarding when sanitizers are used because the + // FIXME: Override value name discarding when asan or msan is used because the // backend passes depend on the name of the alloca in order to print out // names. - Res.getCodeGenOpts().DiscardValueNames &= Res.getLangOpts()->Sanitize.empty(); + Res.getCodeGenOpts().DiscardValueNames &= + !Res.getLangOpts()->Sanitize.has(SanitizerKind::Address) && + !Res.getLangOpts()->Sanitize.has(SanitizerKind::Memory); // FIXME: ParsePreprocessorArgs uses the FileManager to read the contents of // PCH file and find the original header name. Remove the need to do that in From 0c34729b4a4f15327ccd77ef9a1b19ed61a60a35 Mon Sep 17 00:00:00 2001 From: Chris Bieneman Date: Thu, 25 Feb 2016 18:39:19 +0000 Subject: [PATCH 314/742] [CMake] Fixing install-clang-headers dependencies to depend on generating the headers. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261911 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Headers/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Headers/CMakeLists.txt b/lib/Headers/CMakeLists.txt index bbe0688be65..813b7270ab9 100644 --- a/lib/Headers/CMakeLists.txt +++ b/lib/Headers/CMakeLists.txt @@ -109,7 +109,7 @@ install( if (NOT CMAKE_CONFIGURATION_TYPES) # don't add this for IDE's. add_custom_target(install-clang-headers - DEPENDS + DEPENDS clang-headers COMMAND "${CMAKE_COMMAND}" -DCMAKE_INSTALL_COMPONENT=clang-headers -P "${CMAKE_BINARY_DIR}/cmake_install.cmake") From f293f0b00da988eba9e5a0e190e4e093273cf062 Mon Sep 17 00:00:00 2001 From: Chris Bieneman Date: Wed, 23 Mar 2016 01:47:05 +0000 Subject: [PATCH 315/742] [Apple Clang] Expose llvm-config from stage2 builds in stage1 This exposes the stage2-llvm-config target though the stage1 build configuration. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@264125 91177308-0d34-0410-b5e6-96231b3b80d8 --- cmake/caches/Apple-stage1.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/caches/Apple-stage1.cmake b/cmake/caches/Apple-stage1.cmake index a98b2ab8a1f..814cfdf4860 100644 --- a/cmake/caches/Apple-stage1.cmake +++ b/cmake/caches/Apple-stage1.cmake @@ -37,6 +37,7 @@ set(CLANG_BOOTSTRAP_TARGETS check-all check-llvm check-clang + llvm-config test-suite test-depends llvm-test-depends From fc59479613da8855693fb348e00ad6733aa1d70c Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Wed, 23 Mar 2016 16:28:28 +0000 Subject: [PATCH 316/742] ObjC: Handle boolean fixed type for enum. Before this commit, we assert failure in ImplicitCastExpr "unheralded conversion to bool". This commit fixes the assertion by using the correct cast type when the fixed type is boolean. This commit also fixes the behavior for Microsoft mode as well, since Obj-C and Microsoft mode share the same code path. rdar://24999533 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@264167 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/SemaDecl.cpp | 5 ++++- test/SemaObjC/enum-fixed-type.m | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index ac517694b86..e69d0a6d193 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -13927,7 +13927,10 @@ EnumConstantDecl *Sema::CheckEnumConstant(EnumDecl *Enum, } else Diag(IdLoc, diag::err_enumerator_too_large) << EltTy; } else - Val = ImpCastExprToType(Val, EltTy, CK_IntegralCast).get(); + Val = ImpCastExprToType(Val, EltTy, + EltTy->isBooleanType() ? + CK_IntegralToBoolean : CK_IntegralCast) + .get(); } else if (getLangOpts().CPlusPlus) { // C++11 [dcl.enum]p5: // If the underlying type is not fixed, the type of each enumerator diff --git a/test/SemaObjC/enum-fixed-type.m b/test/SemaObjC/enum-fixed-type.m index c00e45a03ed..37d2810a504 100644 --- a/test/SemaObjC/enum-fixed-type.m +++ b/test/SemaObjC/enum-fixed-type.m @@ -38,3 +38,8 @@ void test() { typedef enum : Integer { BaseElem } BaseEnum; typedef enum : BaseEnum { DerivedElem } DerivedEnum; // expected-error {{non-integral type 'BaseEnum' is an invalid underlying type}} + +// +enum MyEnum : _Bool { + MyThing = 0 +}; From 7a88d59ef4ab9b730d2ef18b99ea2da71e299cc5 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Fri, 25 Mar 2016 00:20:35 +0000 Subject: [PATCH 317/742] Debug Info: Add a testcase for the bug introduced by r259975. In r259975 we rauw'ed the scope of enum declarations without taking into account that DIBuilder strips out scope references that point to the DICompileUnit to facilitate type uniquing. This testcase guards against making the same mistake again. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@264366 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 3fc669a306285df6d5dbc0f9bd98eeb5809f84a3) --- test/CodeGenCXX/debug-info-enum-class.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/CodeGenCXX/debug-info-enum-class.cpp b/test/CodeGenCXX/debug-info-enum-class.cpp index 71e6e2b2574..70c57d6cb75 100644 --- a/test/CodeGenCXX/debug-info-enum-class.cpp +++ b/test/CodeGenCXX/debug-info-enum-class.cpp @@ -4,6 +4,7 @@ enum class A { A1=1 }; // underlying type is int by default enum class B: unsigned long { B1=1 }; // underlying type is unsigned long enum C { C1 = 1 }; enum D : short; // enum forward declaration +enum Z : int; A a; B b; C c; @@ -94,6 +95,11 @@ void f2(E) { // CHECK-NOT: offset: // CHECK-SAME: flags: DIFlagFwdDecl +// CHECK: !DICompositeType(tag: DW_TAG_enumeration_type, name: "Z" +// CHECK-NOT: scope: +// CHECK-SAME: flags: DIFlagFwdDecl +void fz() { Z z; } + namespace test5 { // CHECK: !DICompositeType(tag: DW_TAG_enumeration_type, name: "E" // CHECK-SAME: scope: [[TEST5:![0-9]+]] From 236d6421ba5282c391485dd529c170b3edd0d0b5 Mon Sep 17 00:00:00 2001 From: Faisal Vali Date: Mon, 22 Feb 2016 02:24:29 +0000 Subject: [PATCH 318/742] Fix PR24473 : Teach clang to remember to substitute into member variable templates referred to within dependent qualified ids. In passing also fix a semi-related bug that allows access to variable templates through member access notation. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261506 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/SemaExprMember.cpp | 50 ++++++++++++++++++- .../cxx1y-variable-templates_in_class.cpp | 46 ++++++++++++++++- 2 files changed, 93 insertions(+), 3 deletions(-) diff --git a/lib/Sema/SemaExprMember.cpp b/lib/Sema/SemaExprMember.cpp index f62b5a58e84..207bdc49291 100644 --- a/lib/Sema/SemaExprMember.cpp +++ b/lib/Sema/SemaExprMember.cpp @@ -902,6 +902,32 @@ static bool IsInFnTryBlockHandler(const Scope *S) { return false; } +static VarDecl * +getVarTemplateSpecialization(Sema &S, VarTemplateDecl *VarTempl, + const TemplateArgumentListInfo *TemplateArgs, + const DeclarationNameInfo &MemberNameInfo, + SourceLocation TemplateKWLoc) { + + if (!TemplateArgs) { + S.Diag(MemberNameInfo.getBeginLoc(), diag::err_template_decl_ref) + << /*Variable template*/ 1 << MemberNameInfo.getName() + << MemberNameInfo.getSourceRange(); + + S.Diag(VarTempl->getLocation(), diag::note_template_decl_here); + + return nullptr; + } + DeclResult VDecl = S.CheckVarTemplateId( + VarTempl, TemplateKWLoc, MemberNameInfo.getLoc(), *TemplateArgs); + if (VDecl.isInvalid()) + return nullptr; + VarDecl *Var = cast(VDecl.get()); + if (!Var->getTemplateSpecializationKind()) + Var->setTemplateSpecializationKind(TSK_ImplicitInstantiation, + MemberNameInfo.getLoc()); + return Var; +} + ExprResult Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, SourceLocation OpLoc, bool IsArrow, @@ -1069,9 +1095,20 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, // Handle the implicit-member-access case. if (!BaseExpr) { // If this is not an instance member, convert to a non-member access. - if (!MemberDecl->isCXXInstanceMember()) + if (!MemberDecl->isCXXInstanceMember()) { + // If this is a variable template, get the instantiated variable + // declaration corresponding to the supplied template arguments + // (while emitting diagnostics as necessary) that will be referenced + // by this expression. + if (isa(MemberDecl)) { + MemberDecl = getVarTemplateSpecialization( + *this, cast(MemberDecl), TemplateArgs, + R.getLookupNameInfo(), TemplateKWLoc); + if (!MemberDecl) + return ExprError(); + } return BuildDeclarationNameExpr(SS, R.getLookupNameInfo(), MemberDecl); - + } SourceLocation Loc = R.getNameLoc(); if (SS.getRange().isValid()) Loc = SS.getRange().getBegin(); @@ -1127,6 +1164,15 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, TemplateKWLoc, Enum, FoundDecl, MemberNameInfo, Enum->getType(), VK_RValue, OK_Ordinary); } + if (VarTemplateDecl *VarTempl = dyn_cast(MemberDecl)) { + if (VarDecl *Var = getVarTemplateSpecialization( + *this, VarTempl, TemplateArgs, MemberNameInfo, TemplateKWLoc)) + return BuildMemberExpr(*this, Context, BaseExpr, IsArrow, OpLoc, SS, + TemplateKWLoc, Var, FoundDecl, MemberNameInfo, + Var->getType().getNonReferenceType(), VK_LValue, + OK_Ordinary); + return ExprError(); + } // We found something that we didn't expect. Complain. if (isa(MemberDecl)) diff --git a/test/SemaCXX/cxx1y-variable-templates_in_class.cpp b/test/SemaCXX/cxx1y-variable-templates_in_class.cpp index 65e2d6b608c..1c59585b325 100644 --- a/test/SemaCXX/cxx1y-variable-templates_in_class.cpp +++ b/test/SemaCXX/cxx1y-variable-templates_in_class.cpp @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -verify -fsyntax-only %s -Wno-c++11-extensions -Wno-c++1y-extensions -DPRECXX11 // RUN: %clang_cc1 -std=c++11 -verify -fsyntax-only -Wno-c++1y-extensions %s -// RUN: %clang_cc1 -std=c++1y -verify -fsyntax-only %s +// RUN: %clang_cc1 -std=c++1y -verify -fsyntax-only %s -DCPP1Y #define CONST const @@ -338,3 +338,47 @@ namespace b20896909 { A ai; // expected-note {{in instantiation of}} } } +namespace member_access_is_ok { +#ifdef CPP1Y + namespace ns1 { + struct A { + template constexpr static T Var = N; + }; + static_assert(A{}.Var == 5,""); + } // end ns1 +#endif // CPP1Y + +namespace ns2 { + template struct A { + template static T&& Var; + }; + template template T&& A::Var = T(N + M); + int *AV = &A().Var; + +} //end ns2 +} // end ns member_access_is_ok + +#ifdef CPP1Y +namespace PR24473 { +struct Value +{ + template + static constexpr T value = 0; +}; + +template +struct Something +{ + void foo() { + static_assert(TValue::template value == 0, ""); // error + } +}; + +int main() { + Something{}.foo(); + return 0; +} + +} // end ns PR24473 +#endif // CPP1Y + From bc51065aeebe3a8a066c428685548c8f9e3b6ca0 Mon Sep 17 00:00:00 2001 From: Marina Yatsina Date: Tue, 23 Feb 2016 08:53:45 +0000 Subject: [PATCH 319/742] [ms-inline-asm] Fixing bug in single asm statement support Fixing a crash caused by trying to merge a single-line asm statement with an asm block that follows it, e.g: asm int 4 asm { int 5 } Now, only adjacent single-line asm statements that are not surrounded by braces will be merged into one asm call. Differential Revision: http://reviews.llvm.org/D17496 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261618 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Parse/ParseStmtAsm.cpp | 4 ++++ test/CodeGen/ms-inline-asm.c | 13 +++++++++++-- test/Parser/ms-inline-asm.c | 2 +- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/lib/Parse/ParseStmtAsm.cpp b/lib/Parse/ParseStmtAsm.cpp index 11b0c0a86c9..0cc3d051c16 100644 --- a/lib/Parse/ParseStmtAsm.cpp +++ b/lib/Parse/ParseStmtAsm.cpp @@ -424,6 +424,10 @@ StmtResult Parser::ParseMicrosoftAsmStatement(SourceLocation AsmLoc) { // We're no longer in a comment. InAsmComment = false; if (isAsm) { + // If this is a new __asm {} block we want to process it seperately + // from the single-line __asm statements + if (PP.LookAhead(0).is(tok::l_brace)) + break; LineNo = SrcMgr.getLineNumber(ExpLoc.first, ExpLoc.second); SkippedStartOfLine = Tok.isAtStartOfLine(); } diff --git a/test/CodeGen/ms-inline-asm.c b/test/CodeGen/ms-inline-asm.c index 2f5de676c72..e4d9756191d 100644 --- a/test/CodeGen/ms-inline-asm.c +++ b/test/CodeGen/ms-inline-asm.c @@ -63,10 +63,19 @@ void t7() { int t8() { __asm int 4 ; } comments for single-line asm __asm {} - __asm int 4 + __asm { int 5} + __asm int 6 + __asm int 7 + __asm { + int 8 + } return 10; // CHECK: t8 -// CHECK: call i32 asm sideeffect inteldialect "int $$4\0A\09int $$4", "={eax},~{dirflag},~{fpsr},~{flags}"() +// CHECK: call i32 asm sideeffect inteldialect "int $$4", "={eax},~{dirflag},~{fpsr},~{flags}"() +// CHECK: call i32 asm sideeffect inteldialect "", "={eax},~{dirflag},~{fpsr},~{flags}"() +// CHECK: call i32 asm sideeffect inteldialect "int $$5", "={eax},~{dirflag},~{fpsr},~{flags}"() +// CHECK: call i32 asm sideeffect inteldialect "int $$6\0A\09int $$7", "={eax},~{dirflag},~{fpsr},~{flags}"() +// CHECK: call i32 asm sideeffect inteldialect "int $$8", "={eax},~{dirflag},~{fpsr},~{flags}"() // CHECK: ret i32 10 } diff --git a/test/Parser/ms-inline-asm.c b/test/Parser/ms-inline-asm.c index 6dde5f5c0d6..5b0cabf7e6a 100644 --- a/test/Parser/ms-inline-asm.c +++ b/test/Parser/ms-inline-asm.c @@ -55,4 +55,4 @@ void t12() { } int t_fail() { // expected-note {{to match this}} __asm - __asm { // expected-error 2 {{expected}} expected-note {{to match this}} + __asm { // expected-error 3 {{expected}} expected-note {{to match this}} From 4d5c0179dfc25bacb80337fcf59d19631fc25202 Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Fri, 25 Mar 2016 18:43:46 +0000 Subject: [PATCH 320/742] [ObjCXX] Warn undeclared identifiers. Instantiation dependence were not being handled correctly for OpqaueValueExpr AST nodes. As a result, if an undeclared identifier was used in a CXXNewExpr that is assigned to a ObjC property, there would be no error during parsing, and there would be a crash during code gen. This patch makes sure that an error will be issued during parsing in this case. Before the fix, if CXXNewExpr has a typo, its InstantiationDependent will be set to true, but if it is wrapped in a OpaqueValueExpr, the OpaqueValueExpr will not be instantiation dependent, causing the TypoExpr not be to resolved. The fix propagates InstantiationDependent to OpaqueValueExpr from its SourceExpr. It also propagates the other instantiation bits. rdar://24975562 Differential Revision: http://reviews.llvm.org/D18461 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@264444 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Expr.h | 6 ++++-- include/clang/AST/Stmt.h | 1 + test/SemaObjCXX/typo-correction.mm | 23 +++++++++++++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 test/SemaObjCXX/typo-correction.mm diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index bc5dab754c6..6b14c60e951 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -847,10 +847,12 @@ class OpaqueValueExpr : public Expr { ExprObjectKind OK = OK_Ordinary, Expr *SourceExpr = nullptr) : Expr(OpaqueValueExprClass, T, VK, OK, - T->isDependentType(), + T->isDependentType() || + (SourceExpr && SourceExpr->isTypeDependent()), T->isDependentType() || (SourceExpr && SourceExpr->isValueDependent()), - T->isInstantiationDependentType(), + T->isInstantiationDependentType() || + (SourceExpr && SourceExpr->isInstantiationDependent()), false), SourceExpr(SourceExpr), Loc(Loc) { } diff --git a/include/clang/AST/Stmt.h b/include/clang/AST/Stmt.h index d3950e92cf0..b6ed6c547cc 100644 --- a/include/clang/AST/Stmt.h +++ b/include/clang/AST/Stmt.h @@ -115,6 +115,7 @@ class LLVM_ALIGNAS(LLVM_PTR_SIZE) Stmt { friend class OverloadExpr; // ctor friend class PseudoObjectExpr; // ctor friend class AtomicExpr; // ctor + friend class OpaqueValueExpr; // ctor unsigned : NumStmtBits; unsigned ValueKind : 2; diff --git a/test/SemaObjCXX/typo-correction.mm b/test/SemaObjCXX/typo-correction.mm new file mode 100644 index 00000000000..a34a7901e8e --- /dev/null +++ b/test/SemaObjCXX/typo-correction.mm @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 %s -verify -fsyntax-only + +class ClassA {}; + +class ClassB { +public: + ClassB(ClassA* parent=0); + ~ClassB(); +}; + +@interface NSObject +@end + +@interface InterfaceA : NSObject +@property(nonatomic, assign) ClassA *m_prop1; // expected-note {{here}} +@property(nonatomic, assign) ClassB *m_prop2; +@end + +@implementation InterfaceA +- (id)test { + self.m_prop2 = new ClassB(m_prop1); // expected-error {{use of undeclared identifier 'm_prop1'; did you mean '_m_prop1'?}} +} +@end From f18d7c1f3f1c4017531b204a6648b850d3321e85 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Fri, 25 Mar 2016 21:18:22 +0000 Subject: [PATCH 321/742] [analyzer] Add CIFIlter modeling to DeallocChecker. The -dealloc method in CIFilter is highly unusual in that it will release instance variables belonging to its *subclasses* if the variable name starts with "input" or backs a property whose name starts with "input". Subclasses should not release these ivars in their own -dealloc method -- doing so could result in an over release. Before this commit, the DeallocChecker would warn about missing releases for such "input" properties -- which could cause users of the analyzer to add over releases to silence the warning. To avoid this, DeallocChecker now treats CIFilter "input-prefixed" ivars as MustNotReleaseDirectly and so will not require a release. Further, it will now warn when such an ivar is directly released in -dealloc. rdar://problem/25364901 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@264463 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit fcff7954e1aa04b125a9ed9c39dffdeefcfbb18f) --- .../Checkers/CheckObjCDealloc.cpp | 66 +++++++++++-- test/Analysis/DeallocMissingRelease.m | 92 ++++++++++++++++++- ...system-header-simulator-for-objc-dealloc.h | 3 + 3 files changed, 150 insertions(+), 11 deletions(-) diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index 84856bd6dcd..bee9deadd07 100644 --- a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -98,7 +98,8 @@ class ObjCDeallocChecker check::PointerEscape, check::PreStmt> { - mutable IdentifierInfo *NSObjectII, *SenTestCaseII, *Block_releaseII; + mutable IdentifierInfo *NSObjectII, *SenTestCaseII, *Block_releaseII, + *CIFilterII; mutable Selector DeallocSel, ReleaseSel; std::unique_ptr MissingReleaseBugType; @@ -169,6 +170,8 @@ class ObjCDeallocChecker void initIdentifierInfoAndSelectors(ASTContext &Ctx) const; bool classHasSeparateTeardown(const ObjCInterfaceDecl *ID) const; + + bool isReleasedByCIFilterDealloc(const ObjCPropertyImplDecl *PropImpl) const; }; } // End anonymous namespace. @@ -688,19 +691,27 @@ bool ObjCDeallocChecker::diagnoseExtraRelease(SymbolRef ReleasedValue, assert(PropDecl->getSetterKind() == ObjCPropertyDecl::Weak || (PropDecl->getSetterKind() == ObjCPropertyDecl::Assign && - !PropDecl->isReadOnly())); + !PropDecl->isReadOnly()) || + isReleasedByCIFilterDealloc(PropImpl) + ); const ObjCImplDecl *Container = getContainingObjCImpl(C.getLocationContext()); OS << "The '" << *PropImpl->getPropertyIvarDecl() - << "' ivar in '" << *Container - << "' was synthesized for "; + << "' ivar in '" << *Container; - if (PropDecl->getSetterKind() == ObjCPropertyDecl::Weak) - OS << "a weak"; - else - OS << "an assign, readwrite"; - OS << " property but was released in 'dealloc'"; + if (isReleasedByCIFilterDealloc(PropImpl)) { + OS << "' will be released by '-[CIFilter dealloc]' but also released here"; + } else { + OS << "' was synthesized for "; + + if (PropDecl->getSetterKind() == ObjCPropertyDecl::Weak) + OS << "a weak"; + else + OS << "an assign, readwrite"; + + OS << " property but was released in 'dealloc'"; + } std::unique_ptr BR( new BugReport(*ExtraReleaseBugType, OS.str(), ErrNode)); @@ -751,7 +762,7 @@ bool ObjCDeallocChecker::diagnoseMistakenDealloc(SymbolRef DeallocedValue, ObjCDeallocChecker:: ObjCDeallocChecker() - : NSObjectII(nullptr), SenTestCaseII(nullptr) { + : NSObjectII(nullptr), SenTestCaseII(nullptr), CIFilterII(nullptr) { MissingReleaseBugType.reset( new BugType(this, "Missing ivar release (leak)", @@ -774,6 +785,7 @@ void ObjCDeallocChecker::initIdentifierInfoAndSelectors( NSObjectII = &Ctx.Idents.get("NSObject"); SenTestCaseII = &Ctx.Idents.get("SenTestCase"); Block_releaseII = &Ctx.Idents.get("_Block_release"); + CIFilterII = &Ctx.Idents.get("CIFilter"); IdentifierInfo *DeallocII = &Ctx.Idents.get("dealloc"); IdentifierInfo *ReleaseII = &Ctx.Idents.get("release"); @@ -894,6 +906,9 @@ ReleaseRequirement ObjCDeallocChecker::getDeallocReleaseRequirement( // the value in their instance variables must be released in -dealloc. case ObjCPropertyDecl::Retain: case ObjCPropertyDecl::Copy: + if (isReleasedByCIFilterDealloc(PropImpl)) + return ReleaseRequirement::MustNotReleaseDirectly; + return ReleaseRequirement::MustRelease; case ObjCPropertyDecl::Weak: @@ -1019,6 +1034,37 @@ bool ObjCDeallocChecker::classHasSeparateTeardown( return true; } +/// The -dealloc method in CIFilter highly unusual in that is will release +/// instance variables belonging to its *subclasses* if the variable name +/// starts with "input" or backs a property whose name starts with "input". +/// Subclasses should not release these ivars in their own -dealloc method -- +/// doing so could result in an over release. +/// +/// This method returns true if the property will be released by +/// -[CIFilter dealloc]. +bool ObjCDeallocChecker::isReleasedByCIFilterDealloc( + const ObjCPropertyImplDecl *PropImpl) const { + assert(PropImpl->getPropertyIvarDecl()); + StringRef PropName = PropImpl->getPropertyDecl()->getName(); + StringRef IvarName = PropImpl->getPropertyIvarDecl()->getName(); + + const char *ReleasePrefix = "input"; + if (!(PropName.startswith(ReleasePrefix) || + IvarName.startswith(ReleasePrefix))) { + return false; + } + + const ObjCInterfaceDecl *ID = + PropImpl->getPropertyIvarDecl()->getContainingInterface(); + for ( ; ID ; ID = ID->getSuperClass()) { + IdentifierInfo *II = ID->getIdentifier(); + if (II == CIFilterII) + return true; + } + + return false; +} + void ento::registerObjCDeallocChecker(CheckerManager &Mgr) { const LangOptions &LangOpts = Mgr.getLangOpts(); // These checker only makes sense under MRR. diff --git a/test/Analysis/DeallocMissingRelease.m b/test/Analysis/DeallocMissingRelease.m index 26f32db7ff0..01fcc6e7c8e 100644 --- a/test/Analysis/DeallocMissingRelease.m +++ b/test/Analysis/DeallocMissingRelease.m @@ -747,10 +747,100 @@ - (void)dealloc { #if NON_ARC // Only warn for synthesized ivars. - [okToDeallocDirectly dealloc]; // now-warning + [okToDeallocDirectly dealloc]; // no-warning [_ivar dealloc]; // expected-warning {{'_ivar' should be released rather than deallocated}} [super dealloc]; #endif } @end + +// CIFilter special cases. +// By design, -[CIFilter dealloc] releases (by calling -setValue: forKey: with +// 'nil') all ivars (even in its *subclasses*) with names starting with +// 'input' or that are backed by properties with names starting with 'input'. +// The Dealloc checker needs to take particular care to not warn about missing +// releases in this case -- if the user adds a release quiet the +// warning it may result in an over release. + +@interface ImmediateSubCIFilter : CIFilter { + NSObject *inputIvar; + NSObject *nonInputIvar; + NSObject *notPrefixedButBackingPrefixedProperty; + NSObject *inputPrefixedButBackingNonPrefixedProperty; +} + +@property(retain) NSObject *inputIvar; +@property(retain) NSObject *nonInputIvar; +@property(retain) NSObject *inputAutoSynthesizedIvar; +@property(retain) NSObject *inputExplicitlySynthesizedToNonPrefixedIvar; +@property(retain) NSObject *nonPrefixedPropertyBackedByExplicitlySynthesizedPrefixedIvar; + +@end + +@implementation ImmediateSubCIFilter +@synthesize inputIvar = inputIvar; +@synthesize nonInputIvar = nonInputIvar; +@synthesize inputExplicitlySynthesizedToNonPrefixedIvar = notPrefixedButBackingPrefixedProperty; +@synthesize nonPrefixedPropertyBackedByExplicitlySynthesizedPrefixedIvar = inputPrefixedButBackingNonPrefixedProperty; + +- (void)dealloc { +#if NON_ARC + // We don't want warnings here for: + // inputIvar + // inputAutoSynthesizedIvar + // inputExplicitlySynthesizedToNonPrefixedIvar + // inputPrefixedButBackingNonPrefixedProperty + [super dealloc]; + // expected-warning@-1 {{The 'nonInputIvar' ivar in 'ImmediateSubCIFilter' was retained by a synthesized property but not released before '[super dealloc]'}} +#endif +} +@end + +@interface SubSubCIFilter : CIFilter { + NSObject *inputIvarInSub; +} + +@property(retain) NSObject *inputIvarInSub; +@end + +@implementation SubSubCIFilter +@synthesize inputIvarInSub = inputIvarInSub; + +- (void)dealloc { +// Don't warn about inputIvarInSub. +#if NON_ARC + [super dealloc]; +#endif +} +@end +@interface OverreleasingCIFilter : CIFilter { + NSObject *inputIvar; +} + +@property(retain) NSObject *inputIvar; +@end + +@implementation OverreleasingCIFilter +@synthesize inputIvar = inputIvar; + +- (void)dealloc { +#if NON_ARC + // This is an over release because CIFilter's dealloc will also release it. + [inputIvar release]; // expected-warning {{The 'inputIvar' ivar in 'OverreleasingCIFilter' will be released by '-[CIFilter dealloc]' but also released here}} + [super dealloc]; // no-warning + #endif +} +@end + + +@interface NotMissingDeallocCIFilter : CIFilter { + NSObject *inputIvar; +} + +@property(retain) NSObject *inputIvar; +@end + +@implementation NotMissingDeallocCIFilter // no-warning +@synthesize inputIvar = inputIvar; +@end diff --git a/test/Analysis/Inputs/system-header-simulator-for-objc-dealloc.h b/test/Analysis/Inputs/system-header-simulator-for-objc-dealloc.h index 9850aec6ee8..231c0bf5640 100644 --- a/test/Analysis/Inputs/system-header-simulator-for-objc-dealloc.h +++ b/test/Analysis/Inputs/system-header-simulator-for-objc-dealloc.h @@ -30,3 +30,6 @@ typedef struct objc_selector *SEL; void _Block_release(const void *aBlock); #define Block_release(...) _Block_release((const void *)(__VA_ARGS__)) + +@interface CIFilter : NSObject +@end From 6dc3248b4cddccdbe3c6bc7f2b413109d86cc913 Mon Sep 17 00:00:00 2001 From: Matt Arsenault Date: Sun, 13 Mar 2016 05:12:47 +0000 Subject: [PATCH 322/742] Update for new argument to scalbn git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263371 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 9a859e044c3e479a5f8de397410bb8777dfc34ee) --- lib/AST/ExprConstant.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index eaa61ea5c4f..bcb6ba4a91f 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -8513,12 +8513,14 @@ bool ComplexExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { APFloat MaxCD = maxnum(abs(C), abs(D)); if (MaxCD.isFinite()) { DenomLogB = ilogb(MaxCD); - C = scalbn(C, -DenomLogB); - D = scalbn(D, -DenomLogB); + C = scalbn(C, -DenomLogB, APFloat::rmNearestTiesToEven); + D = scalbn(D, -DenomLogB, APFloat::rmNearestTiesToEven); } APFloat Denom = C * C + D * D; - ResR = scalbn((A * C + B * D) / Denom, -DenomLogB); - ResI = scalbn((B * C - A * D) / Denom, -DenomLogB); + ResR = scalbn((A * C + B * D) / Denom, -DenomLogB, + APFloat::rmNearestTiesToEven); + ResI = scalbn((B * C - A * D) / Denom, -DenomLogB, + APFloat::rmNearestTiesToEven); if (ResR.isNaN() && ResI.isNaN()) { if (Denom.isPosZero() && (!A.isNaN() || !B.isNaN())) { ResR = APFloat::getInf(ResR.getSemantics(), C.isNegative()) * A; From 9c5f48978ac7a25f1212fa5d3cf3bd5d1c37c554 Mon Sep 17 00:00:00 2001 From: Bob Wilson Date: Sat, 26 Mar 2016 18:55:13 +0000 Subject: [PATCH 323/742] Check if a path is already absolute before trying to make it so. The FileSystem::makeAbsolute function has been calculating the current working directory unconditionally, even when it is not needed. This calls down to llvm::sys::fs::current_path, which is relatively expensive because it stats two directories, regardless of whether those paths are already in the stat cache. The net effect is that when using the VFS, every stat during header search turns into three stats. With this change, we get back to a single stat for absolute directory paths. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@264519 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Basic/VirtualFileSystem.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/Basic/VirtualFileSystem.cpp b/lib/Basic/VirtualFileSystem.cpp index 6977f400287..822c452b30e 100644 --- a/lib/Basic/VirtualFileSystem.cpp +++ b/lib/Basic/VirtualFileSystem.cpp @@ -99,6 +99,9 @@ FileSystem::getBufferForFile(const llvm::Twine &Name, int64_t FileSize, } std::error_code FileSystem::makeAbsolute(SmallVectorImpl &Path) const { + if (llvm::sys::path::is_absolute(Path)) + return std::error_code(); + auto WorkingDir = getCurrentWorkingDirectory(); if (!WorkingDir) return WorkingDir.getError(); From 990e0f15cffdd7bd527b03627aec23cc498d855e Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 28 Mar 2016 09:31:19 -0700 Subject: [PATCH 324/742] [API Notes] Add support for tags (struct/union/enum/C++ class) and typedefs. Addresses rdar://problem/25365464. --- include/clang/APINotes/APINotesReader.h | 21 ++ include/clang/APINotes/APINotesWriter.h | 12 + include/clang/APINotes/Types.h | 66 +++- lib/APINotes/APINotesFormat.h | 36 ++- lib/APINotes/APINotesReader.cpp | 303 +++++++++++++++++- lib/APINotes/APINotesWriter.cpp | 175 +++++++++- lib/APINotes/APINotesYAMLCompiler.cpp | 216 ++++++++++--- lib/Sema/SemaAPINotes.cpp | 65 +++- .../Inputs/APINotes/HeaderLib.apinotes | 8 + test/APINotes/Inputs/Headers/HeaderLib.h | 3 + test/APINotes/Inputs/roundtrip.apinotes | 12 + test/APINotes/availability.m | 6 + 12 files changed, 834 insertions(+), 89 deletions(-) diff --git a/include/clang/APINotes/APINotesReader.h b/include/clang/APINotes/APINotesReader.h index 4759f85c1ab..16cb5ebe109 100644 --- a/include/clang/APINotes/APINotesReader.h +++ b/include/clang/APINotes/APINotesReader.h @@ -102,6 +102,21 @@ class APINotesReader { /// \returns information about the global function, if known. Optional lookupGlobalFunction(StringRef name); + /// Look for information regarding the given tag + /// (struct/union/enum/C++ class). + /// + /// \param name The name of the tag. + /// + /// \returns information about the tag, if known. + Optional lookupTag(StringRef name); + + /// Look for information regarding the given typedef. + /// + /// \param name The name of the typedef. + /// + /// \returns information about the typedef, if known. + Optional lookupTypedef(StringRef name); + /// Visitor used when walking the contents of the API notes file. class Visitor { public: @@ -131,6 +146,12 @@ class APINotesReader { /// Visit a global function. virtual void visitGlobalFunction(StringRef name, const GlobalFunctionInfo &info); + + /// Visit a tag. + virtual void visitTag(StringRef name, const TagInfo &info); + + /// Visit a typedef. + virtual void visitTypedef(StringRef name, const TypedefInfo &info); }; /// Visit the contents of the API notes file, passing each entity to the diff --git a/include/clang/APINotes/APINotesWriter.h b/include/clang/APINotes/APINotesWriter.h index dca0773aa28..38935a6a271 100644 --- a/include/clang/APINotes/APINotesWriter.h +++ b/include/clang/APINotes/APINotesWriter.h @@ -89,6 +89,18 @@ class APINotesWriter { /// \param name The name of this global function. /// \param info Information about this global function. void addGlobalFunction(StringRef name, const GlobalFunctionInfo &info); + + /// Add information about a tag (struct/union/enum/C++ class). + /// + /// \param name The name of this tag. + /// \param info Information about this tag. + void addTag(StringRef name, const TagInfo &info); + + /// Add information about a typedef. + /// + /// \param name The name of this typedef. + /// \param info Information about this typedef. + void addTypedef(StringRef name, const TypedefInfo &info); }; } // end namespace api_notes diff --git a/include/clang/APINotes/Types.h b/include/clang/APINotes/Types.h index decb137a0f5..5053190f408 100644 --- a/include/clang/APINotes/Types.h +++ b/include/clang/APINotes/Types.h @@ -113,11 +113,43 @@ class CommonEntityInfo { return lhs; } +}; + +/// Describes API notes for types. +class CommonTypeInfo : public CommonEntityInfo { + /// The Swift type to which a given type is bridged. + /// + /// Reflects the swift_bridge attribute. + std::string SwiftBridge; + +public: + CommonTypeInfo() : CommonEntityInfo() { } + + const std::string &getSwiftBridge() const { return SwiftBridge; } + void setSwiftBridge(const std::string &swiftType) { SwiftBridge = swiftType; } + friend CommonTypeInfo &operator|=(CommonTypeInfo &lhs, + const CommonTypeInfo &rhs) { + static_cast(lhs) |= rhs; + if (lhs.SwiftBridge.empty() && !rhs.SwiftBridge.empty()) + lhs.SwiftBridge = rhs.SwiftBridge; + return lhs; + } + + friend bool operator==(const CommonTypeInfo &lhs, + const CommonTypeInfo &rhs) { + return static_cast(lhs) == rhs && + lhs.SwiftBridge == rhs.SwiftBridge; + } + + friend bool operator!=(const CommonTypeInfo &lhs, + const CommonTypeInfo &rhs) { + return !(lhs == rhs); + } }; /// Describes API notes data for an Objective-C class or protocol. -class ObjCContextInfo : public CommonEntityInfo { +class ObjCContextInfo : public CommonTypeInfo { /// Whether this class has a default nullability. unsigned HasDefaultNullability : 1; @@ -127,14 +159,9 @@ class ObjCContextInfo : public CommonEntityInfo { /// Whether this class has designated initializers recorded. unsigned HasDesignatedInits : 1; - /// The Swift type to which a given Objective-C class is bridged. - /// - /// Reflects the swift_bridge attribute. - std::string SwiftBridge; - public: ObjCContextInfo() - : CommonEntityInfo(), + : CommonTypeInfo(), HasDefaultNullability(0), DefaultNullability(0), HasDesignatedInits(0) @@ -167,15 +194,11 @@ class ObjCContextInfo : public CommonEntityInfo { DefaultNullability = 0; } - const std::string &getSwiftBridge() const { return SwiftBridge; } - void setSwiftBridge(const std::string &swiftType) { SwiftBridge = swiftType; } - friend bool operator==(const ObjCContextInfo &lhs, const ObjCContextInfo &rhs) { - return static_cast(lhs) == rhs && + return static_cast(lhs) == rhs && lhs.HasDefaultNullability == rhs.HasDefaultNullability && lhs.DefaultNullability == rhs.DefaultNullability && - lhs.HasDesignatedInits == rhs.HasDesignatedInits && - lhs.SwiftBridge == rhs.SwiftBridge; + lhs.HasDesignatedInits == rhs.HasDesignatedInits; } friend bool operator!=(const ObjCContextInfo &lhs, const ObjCContextInfo &rhs) { @@ -185,7 +208,7 @@ class ObjCContextInfo : public CommonEntityInfo { friend ObjCContextInfo &operator|=(ObjCContextInfo &lhs, const ObjCContextInfo &rhs) { // Merge inherited info. - static_cast(lhs) |= rhs; + static_cast(lhs) |= rhs; // Merge nullability. if (!lhs.getDefaultNullability()) { @@ -196,9 +219,6 @@ class ObjCContextInfo : public CommonEntityInfo { lhs.HasDesignatedInits |= rhs.HasDesignatedInits; - if (lhs.SwiftBridge.empty() && !rhs.SwiftBridge.empty()) - lhs.SwiftBridge = rhs.SwiftBridge; - return lhs; } @@ -436,6 +456,18 @@ class GlobalFunctionInfo : public FunctionInfo { GlobalFunctionInfo() : FunctionInfo() { } }; +/// Describes API notes data for a tag. +class TagInfo : public CommonTypeInfo { +public: + TagInfo() : CommonTypeInfo() { } +}; + +/// Describes API notes data for a typedef. +class TypedefInfo : public CommonTypeInfo { +public: + TypedefInfo() : CommonTypeInfo() { } +}; + } // end namespace api_notes } // end namespace clang diff --git a/lib/APINotes/APINotesFormat.h b/lib/APINotes/APINotesFormat.h index 79241042951..4cdad891841 100644 --- a/lib/APINotes/APINotesFormat.h +++ b/lib/APINotes/APINotesFormat.h @@ -35,7 +35,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 8; +const uint16_t VERSION_MINOR = 9; using IdentifierID = Fixnum<31>; using IdentifierIDField = BCVBR<16>; @@ -84,7 +84,15 @@ enum BlockID { /// The (global) functions data block, which maps global function names to /// information about the global function. - GLOBAL_FUNCTION_BLOCK_ID + GLOBAL_FUNCTION_BLOCK_ID, + + /// The tag data block, which maps tag names to information about + /// the tags. + TAG_BLOCK_ID, + + /// The typedef data block, which maps typedef names to information about + /// the typedefs. + TYPEDEF_BLOCK_ID, }; namespace control_block { @@ -193,6 +201,30 @@ namespace global_function_block { >; } +namespace tag_block { + enum { + TAG_DATA = 1 + }; + + using TagDataLayout = BCRecordLayout< + TAG_DATA, // record ID + BCVBR<16>, // table offset within the blob (see below) + BCBlob // map from name to tag information + >; +}; + +namespace typedef_block { + enum { + TYPEDEF_DATA = 1 + }; + + using TypedefDataLayout = BCRecordLayout< + TYPEDEF_DATA, // record ID + BCVBR<16>, // table offset within the blob (see below) + BCBlob // map from name to typedef information + >; +}; + /// A stored Objective-C selector. struct StoredObjCSelector { unsigned NumPieces; diff --git a/lib/APINotes/APINotesReader.cpp b/lib/APINotes/APINotesReader.cpp index 2d8fcb70690..b4d8f5750e4 100644 --- a/lib/APINotes/APINotesReader.cpp +++ b/lib/APINotes/APINotesReader.cpp @@ -48,6 +48,17 @@ namespace { data += swiftNameLength; } + /// Read serialized CommonTypeInfo. + void readCommonTypeInfo(const uint8_t *&data, CommonTypeInfo &info) { + readCommonEntityInfo(data, info); + + unsigned swiftBridgeLength = + endian::readNext(data); + info.setSwiftBridge( + StringRef(reinterpret_cast(data), swiftBridgeLength)); + data += swiftBridgeLength; + } + /// Used to deserialize the on-disk identifier table. class IdentifierTableInfo { public: @@ -134,19 +145,12 @@ namespace { unsigned length) { data_type result; result.first = endian::readNext(data); - readCommonEntityInfo(data, result.second); + readCommonTypeInfo(data, result.second); if (*data++) { result.second.setDefaultNullability(static_cast(*data)); } ++data; result.second.setHasDesignatedInits(*data++); - - // swift bridge. - unsigned swiftBridgeLength = - endian::readNext(data); - result.second.setSwiftBridge( - StringRef(reinterpret_cast(data), swiftBridgeLength)); - data += swiftBridgeLength; return result; } @@ -410,6 +414,96 @@ namespace { return info; } }; + + /// Used to deserialize the on-disk tag table. + class TagTableInfo { + public: + using internal_key_type = unsigned; // name ID + using external_key_type = internal_key_type; + using data_type = TagInfo; + using hash_value_type = size_t; + using offset_type = unsigned; + + internal_key_type GetInternalKey(external_key_type key) { + return key; + } + + external_key_type GetExternalKey(internal_key_type key) { + return key; + } + + hash_value_type ComputeHash(internal_key_type key) { + return static_cast(llvm::hash_value(key)); + } + + static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { + return lhs == rhs; + } + + static std::pair + ReadKeyDataLength(const uint8_t *&data) { + unsigned keyLength = endian::readNext(data); + unsigned dataLength = endian::readNext(data); + return { keyLength, dataLength }; + } + + static internal_key_type ReadKey(const uint8_t *data, unsigned length) { + auto nameID = endian::readNext(data); + return nameID; + } + + static data_type ReadData(internal_key_type key, const uint8_t *data, + unsigned length) { + TagInfo info; + readCommonTypeInfo(data, info); + return info; + } + }; + + /// Used to deserialize the on-disk typedef table. + class TypedefTableInfo { + public: + using internal_key_type = unsigned; // name ID + using external_key_type = internal_key_type; + using data_type = TypedefInfo; + using hash_value_type = size_t; + using offset_type = unsigned; + + internal_key_type GetInternalKey(external_key_type key) { + return key; + } + + external_key_type GetExternalKey(internal_key_type key) { + return key; + } + + hash_value_type ComputeHash(internal_key_type key) { + return static_cast(llvm::hash_value(key)); + } + + static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { + return lhs == rhs; + } + + static std::pair + ReadKeyDataLength(const uint8_t *&data) { + unsigned keyLength = endian::readNext(data); + unsigned dataLength = endian::readNext(data); + return { keyLength, dataLength }; + } + + static internal_key_type ReadKey(const uint8_t *data, unsigned length) { + auto nameID = endian::readNext(data); + return nameID; + } + + static data_type ReadData(internal_key_type key, const uint8_t *data, + unsigned length) { + TypedefInfo info; + readCommonTypeInfo(data, info); + return info; + } + }; } // end anonymous namespace class APINotesReader::Implementation { @@ -465,6 +559,18 @@ class APINotesReader::Implementation { /// The global function table. std::unique_ptr GlobalFunctionTable; + using SerializedTagTable = + llvm::OnDiskIterableChainedHashTable; + + /// The tag table. + std::unique_ptr TagTable; + + using SerializedTypedefTable = + llvm::OnDiskIterableChainedHashTable; + + /// The typedef table. + std::unique_ptr TypedefTable; + /// Retrieve the identifier ID for the given string, or an empty /// optional if the string is unknown. Optional getIdentifier(StringRef str); @@ -489,6 +595,10 @@ class APINotesReader::Implementation { SmallVectorImpl &scratch); bool readGlobalFunctionBlock(llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch); + bool readTagBlock(llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch); + bool readTypedefBlock(llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch); }; Optional APINotesReader::Implementation::getIdentifier( @@ -959,6 +1069,112 @@ bool APINotesReader::Implementation::readGlobalFunctionBlock( return false; } +bool APINotesReader::Implementation::readTagBlock( + llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch) { + if (cursor.EnterSubBlock(TAG_BLOCK_ID)) + return true; + + auto next = cursor.advance(); + while (next.Kind != llvm::BitstreamEntry::EndBlock) { + if (next.Kind == llvm::BitstreamEntry::Error) + return true; + + if (next.Kind == llvm::BitstreamEntry::SubBlock) { + // Unknown sub-block, possibly for use by a future version of the + // API notes format. + if (cursor.SkipBlock()) + return true; + + next = cursor.advance(); + continue; + } + + scratch.clear(); + StringRef blobData; + unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + switch (kind) { + case tag_block::TAG_DATA: { + // Already saw tag table. + if (TagTable) + return true; + + uint32_t tableOffset; + tag_block::TagDataLayout::readRecord(scratch, tableOffset); + auto base = reinterpret_cast(blobData.data()); + + TagTable.reset( + SerializedTagTable::Create(base + tableOffset, + base + sizeof(uint32_t), + base)); + break; + } + + default: + // Unknown record, possibly for use by a future version of the + // module format. + break; + } + + next = cursor.advance(); + } + + return false; +} + +bool APINotesReader::Implementation::readTypedefBlock( + llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch) { + if (cursor.EnterSubBlock(TYPEDEF_BLOCK_ID)) + return true; + + auto next = cursor.advance(); + while (next.Kind != llvm::BitstreamEntry::EndBlock) { + if (next.Kind == llvm::BitstreamEntry::Error) + return true; + + if (next.Kind == llvm::BitstreamEntry::SubBlock) { + // Unknown sub-block, possibly for use by a future version of the + // API notes format. + if (cursor.SkipBlock()) + return true; + + next = cursor.advance(); + continue; + } + + scratch.clear(); + StringRef blobData; + unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + switch (kind) { + case typedef_block::TYPEDEF_DATA: { + // Already saw typedef table. + if (TypedefTable) + return true; + + uint32_t tableOffset; + typedef_block::TypedefDataLayout::readRecord(scratch, tableOffset); + auto base = reinterpret_cast(blobData.data()); + + TypedefTable.reset( + SerializedTypedefTable::Create(base + tableOffset, + base + sizeof(uint32_t), + base)); + break; + } + + default: + // Unknown record, possibly for use by a future version of the + // module format. + break; + } + + next = cursor.advance(); + } + + return false; +} + APINotesReader::APINotesReader(std::unique_ptr inputBuffer, bool &failed) : Impl(*new Implementation) @@ -1057,6 +1273,20 @@ APINotesReader::APINotesReader(std::unique_ptr inputBuffer, } break; + case TAG_BLOCK_ID: + if (!hasValidControlBlock || Impl.readTagBlock(cursor, scratch)) { + failed = true; + return; + } + break; + + case TYPEDEF_BLOCK_ID: + if (!hasValidControlBlock || Impl.readTypedefBlock(cursor, scratch)) { + failed = true; + return; + } + break; + default: // Unknown top-level block, possibly for use by a future version of the // module format. @@ -1197,6 +1427,37 @@ Optional APINotesReader::lookupGlobalFunction( return *known; } + +Optional APINotesReader::lookupTag(StringRef name) { + if (!Impl.TagTable) + return None; + + Optional nameID = Impl.getIdentifier(name); + if (!nameID) + return None; + + auto known = Impl.TagTable->find(*nameID); + if (known == Impl.TagTable->end()) + return None; + + return *known; +} + +Optional APINotesReader::lookupTypedef(StringRef name) { + if (!Impl.TypedefTable) + return None; + + Optional nameID = Impl.getIdentifier(name); + if (!nameID) + return None; + + auto known = Impl.TypedefTable->find(*nameID); + if (known == Impl.TypedefTable->end()) + return None; + + return *known; +} + APINotesReader::Visitor::~Visitor() { } void APINotesReader::Visitor::visitObjCClass(ContextID contextID, @@ -1224,6 +1485,14 @@ void APINotesReader::Visitor::visitGlobalFunction( StringRef name, const GlobalFunctionInfo &info) { } +void APINotesReader::Visitor::visitTag( + StringRef name, + const TagInfo &info) { } + +void APINotesReader::Visitor::visitTypedef( + StringRef name, + const TypedefInfo &info) { } + void APINotesReader::visit(Visitor &visitor) { // FIXME: All of these iterations would be significantly more efficient if we // could get the keys and data together, but OnDiskIterableHashTable doesn't @@ -1310,5 +1579,23 @@ void APINotesReader::visit(Visitor &visitor) { visitor.visitGlobalVariable(name, info); } } + + // Visit tags. + if (Impl.TagTable) { + for (auto key : Impl.TagTable->keys()) { + auto name = identifiers[key]; + auto info = *Impl.TagTable->find(key); + visitor.visitTag(name, info); + } + } + + // Visit typedefs. + if (Impl.TypedefTable) { + for (auto key : Impl.TypedefTable->keys()) { + auto name = identifiers[key]; + auto info = *Impl.TypedefTable->find(key); + visitor.visitTypedef(name, info); + } + } } diff --git a/lib/APINotes/APINotesWriter.cpp b/lib/APINotes/APINotesWriter.cpp index 102932fdad0..bc17773ed60 100644 --- a/lib/APINotes/APINotesWriter.cpp +++ b/lib/APINotes/APINotesWriter.cpp @@ -77,6 +77,16 @@ class APINotesWriter::Implementation { /// Indexed by the identifier ID. llvm::DenseMap GlobalFunctions; + /// Information about tags. + /// + /// Indexed by the identifier ID. + llvm::DenseMap Tags; + + /// Information about typedefs. + /// + /// Indexed by the identifier ID. + llvm::DenseMap Typedefs; + /// Retrieve the ID for the given identifier. IdentifierID getIdentifier(StringRef identifier) { if (identifier.empty()) @@ -123,6 +133,8 @@ class APINotesWriter::Implementation { void writeObjCSelectorBlock(llvm::BitstreamWriter &writer); void writeGlobalVariableBlock(llvm::BitstreamWriter &writer); void writeGlobalFunctionBlock(llvm::BitstreamWriter &writer); + void writeTagBlock(llvm::BitstreamWriter &writer); + void writeTypedefBlock(llvm::BitstreamWriter &writer); }; /// Record the name of a block. @@ -279,6 +291,20 @@ namespace { out.write(info.SwiftName.c_str(), info.SwiftName.size()); } + // Retrieve the serialized size of the given CommonTypeInfo, for use + // in on-disk hash tables. + static unsigned getCommonTypeInfoSize(const CommonTypeInfo &info) { + return 2 + info.getSwiftBridge().size() + getCommonEntityInfoSize(info); + } + + /// Emit a serialized representation of the common type information. + static void emitCommonTypeInfo(raw_ostream &out, const CommonTypeInfo &info) { + emitCommonEntityInfo(out, info); + endian::Writer writer(out); + writer.write(info.getSwiftBridge().size()); + out.write(info.getSwiftBridge().c_str(), info.getSwiftBridge().size()); + } + /// Used to serialize the on-disk Objective-C context table. class ObjCContextTableInfo { public: @@ -301,9 +327,8 @@ namespace { data_type_ref data) { uint32_t keyLength = sizeof(IdentifierID) + 1; uint32_t dataLength = sizeof(ContextID) - + getCommonEntityInfoSize(data.second) - + dataBytes - + 2 + data.second.getSwiftBridge().size(); + + getCommonTypeInfoSize(data.second) + + dataBytes; endian::Writer writer(out); writer.write(keyLength); writer.write(dataLength); @@ -321,7 +346,7 @@ namespace { endian::Writer writer(out); writer.write(data.first); - emitCommonEntityInfo(out, data.second); + emitCommonTypeInfo(out, data.second); // FIXME: Inefficient representation. uint8_t bytes[dataBytes] = { 0, 0, 0 }; @@ -334,10 +359,6 @@ namespace { bytes[2] = data.second.hasDesignatedInits(); out.write(reinterpret_cast(bytes), dataBytes); - - writer.write(data.second.getSwiftBridge().size()); - out.write(data.second.getSwiftBridge().data(), - data.second.getSwiftBridge().size()); } }; } // end anonymous namespace @@ -733,6 +754,130 @@ void APINotesWriter::Implementation::writeGlobalFunctionBlock( layout.emit(ScratchRecord, tableOffset, hashTableBlob); } +namespace { + /// Used to serialize the on-disk tag table. + class TagTableInfo { + public: + using key_type = unsigned; // name ID + using key_type_ref = key_type; + using data_type = TagInfo; + using data_type_ref = const data_type &; + using hash_value_type = size_t; + using offset_type = unsigned; + + hash_value_type ComputeHash(key_type_ref key) { + return static_cast(llvm::hash_value(key)); + } + + std::pair EmitKeyDataLength(raw_ostream &out, + key_type_ref key, + data_type_ref data) { + uint32_t keyLength = sizeof(IdentifierID); + uint32_t dataLength = getCommonTypeInfoSize(data); + endian::Writer writer(out); + writer.write(keyLength); + writer.write(dataLength); + return { keyLength, dataLength }; + } + + void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { + endian::Writer writer(out); + writer.write(key); + } + + void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, + unsigned len) { + emitCommonTypeInfo(out, data); + } + }; +} // end anonymous namespace + +void APINotesWriter::Implementation::writeTagBlock( + llvm::BitstreamWriter &writer) { + BCBlockRAII restoreBlock(writer, TAG_BLOCK_ID, 3); + + if (Tags.empty()) + return; + + llvm::SmallString<4096> hashTableBlob; + uint32_t tableOffset; + { + llvm::OnDiskChainedHashTableGenerator generator; + for (auto &entry : Tags) + generator.insert(entry.first, entry.second); + + llvm::raw_svector_ostream blobStream(hashTableBlob); + // Make sure that no bucket is at offset 0 + endian::Writer(blobStream).write(0); + tableOffset = generator.Emit(blobStream); + } + + tag_block::TagDataLayout layout(writer); + layout.emit(ScratchRecord, tableOffset, hashTableBlob); +} + +namespace { + /// Used to serialize the on-disk typedef table. + class TypedefTableInfo { + public: + using key_type = unsigned; // name ID + using key_type_ref = key_type; + using data_type = TypedefInfo; + using data_type_ref = const data_type &; + using hash_value_type = size_t; + using offset_type = unsigned; + + hash_value_type ComputeHash(key_type_ref key) { + return static_cast(llvm::hash_value(key)); + } + + std::pair EmitKeyDataLength(raw_ostream &out, + key_type_ref key, + data_type_ref data) { + uint32_t keyLength = sizeof(IdentifierID); + uint32_t dataLength = getCommonTypeInfoSize(data); + endian::Writer writer(out); + writer.write(keyLength); + writer.write(dataLength); + return { keyLength, dataLength }; + } + + void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { + endian::Writer writer(out); + writer.write(key); + } + + void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, + unsigned len) { + emitCommonTypeInfo(out, data); + } + }; +} // end anonymous namespace + +void APINotesWriter::Implementation::writeTypedefBlock( + llvm::BitstreamWriter &writer) { + BCBlockRAII restoreBlock(writer, TYPEDEF_BLOCK_ID, 3); + + if (Typedefs.empty()) + return; + + llvm::SmallString<4096> hashTableBlob; + uint32_t tableOffset; + { + llvm::OnDiskChainedHashTableGenerator generator; + for (auto &entry : Typedefs) + generator.insert(entry.first, entry.second); + + llvm::raw_svector_ostream blobStream(hashTableBlob); + // Make sure that no bucket is at offset 0 + endian::Writer(blobStream).write(0); + tableOffset = generator.Emit(blobStream); + } + + typedef_block::TypedefDataLayout layout(writer); + layout.emit(ScratchRecord, tableOffset, hashTableBlob); +} + void APINotesWriter::Implementation::writeToStream(llvm::raw_ostream &os) { // Write the API notes file into a buffer. SmallVector buffer; @@ -753,6 +898,8 @@ void APINotesWriter::Implementation::writeToStream(llvm::raw_ostream &os) { writeObjCSelectorBlock(writer); writeGlobalVariableBlock(writer); writeGlobalFunctionBlock(writer); + writeTagBlock(writer); + writeTypedefBlock(writer); } // Write the buffer to the stream. @@ -856,3 +1003,15 @@ void APINotesWriter::addGlobalFunction(llvm::StringRef name, assert(!Impl.GlobalFunctions.count(nameID)); Impl.GlobalFunctions[nameID] = info; } + +void APINotesWriter::addTag(llvm::StringRef name, const TagInfo &info) { + IdentifierID tagID = Impl.getIdentifier(name); + assert(!Impl.Tags.count(tagID)); + Impl.Tags[tagID] = info; +} + +void APINotesWriter::addTypedef(llvm::StringRef name, const TypedefInfo &info) { + IdentifierID typedefID = Impl.getIdentifier(name); + assert(!Impl.Typedefs.count(typedefID)); + Impl.Typedefs[typedefID] = info; +} diff --git a/lib/APINotes/APINotesYAMLCompiler.cpp b/lib/APINotes/APINotesYAMLCompiler.cpp index 17d8fab3637..39f6c9d6410 100644 --- a/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/lib/APINotes/APINotesYAMLCompiler.cpp @@ -57,6 +57,10 @@ ... Globals: # List of globals ... + Tags: # List of tags (struct/union/enum/C++ class) + ... + Typedefs: # List of typedef-names and C++11 type aliases + ... Each class and protocol is defined as following: @@ -212,6 +216,22 @@ namespace { }; typedef std::vector GlobalVariablesSeq; + struct Tag { + StringRef Name; + AvailabilityItem Availability; + StringRef SwiftName; + StringRef SwiftBridge; + }; + typedef std::vector TagsSeq; + + struct Typedef { + StringRef Name; + AvailabilityItem Availability; + StringRef SwiftName; + StringRef SwiftBridge; + }; + typedef std::vector TypedefsSeq; + struct Module { StringRef Name; AvailabilityItem Availability; @@ -219,6 +239,8 @@ namespace { ClassesSeq Protocols; FunctionsSeq Functions; GlobalVariablesSeq Globals; + TagsSeq Tags; + TypedefsSeq Typedefs; LLVM_ATTRIBUTE_DEPRECATED( void dump() LLVM_ATTRIBUTE_USED, @@ -232,6 +254,8 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(Property) LLVM_YAML_IS_SEQUENCE_VECTOR(Class) LLVM_YAML_IS_SEQUENCE_VECTOR(Function) LLVM_YAML_IS_SEQUENCE_VECTOR(GlobalVariable) +LLVM_YAML_IS_SEQUENCE_VECTOR(Tag) +LLVM_YAML_IS_SEQUENCE_VECTOR(Typedef) namespace llvm { namespace yaml { @@ -345,6 +369,28 @@ namespace llvm { } }; + template <> + struct MappingTraits { + static void mapping(IO &io, Tag& t) { + io.mapRequired("Name", t.Name); + io.mapOptional("Availability", t.Availability.Mode); + io.mapOptional("AvailabilityMsg", t.Availability.Msg); + io.mapOptional("SwiftName", t.SwiftName); + io.mapOptional("SwiftBridge", t.SwiftBridge); + } + }; + + template <> + struct MappingTraits { + static void mapping(IO &io, Typedef& t) { + io.mapRequired("Name", t.Name); + io.mapOptional("Availability", t.Availability.Mode); + io.mapOptional("AvailabilityMsg", t.Availability.Msg); + io.mapOptional("SwiftName", t.SwiftName); + io.mapOptional("SwiftBridge", t.SwiftBridge); + } + }; + template <> struct MappingTraits { static void mapping(IO &io, Module& m) { @@ -355,6 +401,8 @@ namespace llvm { io.mapOptional("Protocols", m.Protocols); io.mapOptional("Functions", m.Functions); io.mapOptional("Globals", m.Globals); + io.mapOptional("Tags", m.Tags); + io.mapOptional("Typedefs", m.Typedefs); } }; } @@ -377,11 +425,7 @@ static bool parseAPINotes(StringRef yamlInput, Module &module, return static_cast(yin.error()); } -static bool compile(const Module &module, - llvm::raw_ostream &os, - api_notes::OSType targetOS, - llvm::SourceMgr::DiagHandlerTy diagHandler, - void *diagHandlerCtxt){ +namespace { using namespace api_notes; class YAMLConverter { @@ -431,7 +475,7 @@ static bool compile(const Module &module, outInfo.UnavailableMsg = in.Msg; } else { if (!in.Msg.empty()) { - emitError("availability message for available class '" + + emitError("availability message for available API '" + apiName + "' will not be used"); } } @@ -466,17 +510,37 @@ static bool compile(const Module &module, } } + /// Convert the common parts of an entity from YAML. + template + bool convertCommon(const T& common, CommonEntityInfo &info, + StringRef apiName) { + if (!isAvailable(common.Availability)) + return true; + + convertAvailability(common.Availability, info, apiName); + info.SwiftName = common.SwiftName; + return false; + } + + /// Convert the common parts of a type entity from YAML. + template + bool convertCommonType(const T& common, CommonTypeInfo &info, + StringRef apiName) { + if (convertCommon(common, info, apiName)) + return true; + + info.setSwiftBridge(common.SwiftBridge); + return false; + } + // Translate from Method into ObjCMethodInfo and write it out. void convertMethod(const Method &meth, ContextID classID, StringRef className) { ObjCMethodInfo mInfo; - if (!isAvailable(meth.Availability)) + if (convertCommon(meth, mInfo, meth.Selector)) return; - convertAvailability(meth.Availability, mInfo, meth.Selector); - mInfo.SwiftName = meth.SwiftName; - // Check if the selector ends with ':' to determine if it takes arguments. bool takesArguments = meth.Selector.endswith(":"); @@ -505,27 +569,20 @@ static bool compile(const Module &module, // Write it. Writer->addObjCMethod(classID, selectorRef, - meth.Kind == MethodKind::Instance, - mInfo); + meth.Kind == MethodKind::Instance, + mInfo); } void convertContext(const Class &cl, bool isClass) { // Write the class. ObjCContextInfo cInfo; - // First, translate and check availability info. - if (!isAvailable(cl.Availability)) + if (convertCommonType(cl, cInfo, cl.Name)) return; - convertAvailability(cl.Availability, cInfo, cl.Name); - cInfo.SwiftName = cl.SwiftName; - if (cl.AuditedForNullability) cInfo.setDefaultNullability(*DefaultNullability); - if (isClass) - cInfo.setSwiftBridge(cl.SwiftBridge); - ContextID clID = isClass ? Writer->addObjCClass(cl.Name, cInfo) : Writer->addObjCProtocol(cl.Name, cInfo); @@ -644,12 +701,52 @@ static bool compile(const Module &module, Writer->addGlobalFunction(function.Name, info); } + // Write all tags. + llvm::StringSet<> knownTags; + for (const auto &t : TheModule.Tags) { + // Check for duplicate tag definitions. + if (!knownTags.insert(t.Name).second) { + emitError("multiple definitions of tag '" + t.Name + "'"); + continue; + } + + TagInfo tagInfo; + if (convertCommonType(t, tagInfo, t.Name)) + continue; + + Writer->addTag(t.Name, tagInfo); + } + + // Write all typedefs. + llvm::StringSet<> knownTypedefs; + for (const auto &t : TheModule.Typedefs) { + // Check for duplicate typedef definitions. + if (!knownTags.insert(t.Name).second) { + emitError("multiple definitions of typedef '" + t.Name + "'"); + continue; + } + + TypedefInfo typedefInfo; + if (convertCommonType(t, typedefInfo, t.Name)) + continue; + + Writer->addTypedef(t.Name, typedefInfo); + } + if (!ErrorOccured) Writer->writeToStream(OS); return ErrorOccured; } }; +} + +static bool compile(const Module &module, + llvm::raw_ostream &os, + api_notes::OSType targetOS, + llvm::SourceMgr::DiagHandlerTy diagHandler, + void *diagHandlerCtxt){ + using namespace api_notes; YAMLConverter c(module, targetOS, os, diagHandler, diagHandlerCtxt); return c.convertModule(); @@ -689,15 +786,7 @@ bool api_notes::compileAPINotes(StringRef yamlInput, return compile(module, os, targetOS, diagHandler, diagHandlerCtxt); } -bool api_notes::decompileAPINotes(std::unique_ptr input, - llvm::raw_ostream &os) { - // Try to read the file. - auto reader = APINotesReader::get(std::move(input)); - if (!reader) { - llvm::errs() << "not a well-formed API notes binary file\n"; - return true; - } - +namespace { // Deserialize the API notes file into a module. class DecompileVisitor : public APINotesReader::Visitor { /// Allocator used to clone those strings that need it. @@ -720,20 +809,28 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, return StringRef(reinterpret_cast(ptr), string.size()); } + template + void handleCommon(T &record, const CommonEntityInfo &info) { + handleAvailability(record.Availability, info); + record.SwiftName = copyString(info.SwiftName); + } + + template + void handleCommonType(T &record, const CommonTypeInfo &info) { + handleCommon(record, info); + record.SwiftBridge = copyString(info.getSwiftBridge()); + } + /// Map Objective-C context info. void handleObjCContext(Class &record, StringRef name, const ObjCContextInfo &info) { record.Name = name; - // Handle class information. - handleAvailability(record.Availability, info); - record.SwiftName = copyString(info.SwiftName); + handleCommonType(record, info); if (info.getDefaultNullability()) { record.AuditedForNullability = true; } - - record.SwiftBridge = copyString(info.getSwiftBridge()); } /// Map availability information, if present. @@ -792,10 +889,9 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, method.Selector = copyString(selector); method.Kind = isInstanceMethod ? MethodKind::Instance : MethodKind::Class; + handleCommon(method, info); handleNullability(method.Nullability, method.NullabilityOfRet, info, selector.count(':')); - handleAvailability(method.Availability, info); - method.SwiftName = copyString(info.SwiftName); method.FactoryAsInit = info.getFactoryAsInitKind(); method.DesignatedInit = info.DesignatedInit; method.Required = info.Required; @@ -811,8 +907,7 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, const ObjCPropertyInfo &info) { Property property; property.Name = name; - handleAvailability(property.Availability, info); - property.SwiftName = copyString(info.SwiftName); + handleCommon(property, info); // FIXME: No way to represent "not audited for nullability". if (auto nullability = info.getNullability()) { @@ -830,8 +925,7 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, const GlobalFunctionInfo &info) { Function function; function.Name = name; - handleAvailability(function.Availability, info); - function.SwiftName = copyString(info.SwiftName); + handleCommon(function, info); if (info.NumAdjustedNullable > 0) handleNullability(function.Nullability, function.NullabilityOfRet, info, info.NumAdjustedNullable-1); @@ -843,8 +937,7 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, const GlobalVariableInfo &info) { GlobalVariable global; global.Name = name; - handleAvailability(global.Availability, info); - global.SwiftName = copyString(info.SwiftName); + handleCommon(global, info); // FIXME: No way to represent "not audited for nullability". if (auto nullability = info.getNullability()) { @@ -854,10 +947,35 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, TheModule.Globals.push_back(global); } + virtual void visitTag(StringRef name, const TagInfo &info) { + Tag tag; + tag.Name = name; + handleCommonType(tag, info); + TheModule.Tags.push_back(tag); + } + + virtual void visitTypedef(StringRef name, const TypedefInfo &info) { + Typedef td; + td.Name = name; + handleCommonType(td, info); + TheModule.Typedefs.push_back(td); + } + /// Retrieve the module. Module &getModule() { return TheModule; } - } decompileVisitor; + }; +} + +bool api_notes::decompileAPINotes(std::unique_ptr input, + llvm::raw_ostream &os) { + // Try to read the file. + auto reader = APINotesReader::get(std::move(input)); + if (!reader) { + llvm::errs() << "not a well-formed API notes binary file\n"; + return true; + } + DecompileVisitor decompileVisitor; reader->visit(decompileVisitor); // Sort the data in the module, because the API notes reader doesn't preserve @@ -911,6 +1029,18 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, return lhs.Name < rhs.Name; }); + // Sort tags. + std::sort(module.Tags.begin(), module.Tags.end(), + [](const Tag &lhs, const Tag &rhs) -> bool { + return lhs.Name < rhs.Name; + }); + + // Sort typedefs. + std::sort(module.Typedefs.begin(), module.Typedefs.end(), + [](const Typedef &lhs, const Typedef &rhs) -> bool { + return lhs.Name < rhs.Name; + }); + // Output the YAML representation. Output yout(os); yout << module; diff --git a/lib/Sema/SemaAPINotes.cpp b/lib/Sema/SemaAPINotes.cpp index 22b29f4f24c..b7827643d63 100644 --- a/lib/Sema/SemaAPINotes.cpp +++ b/lib/Sema/SemaAPINotes.cpp @@ -128,6 +128,20 @@ static void ProcessAPINotes(Sema &S, Decl *D, } } +static void ProcessAPINotes(Sema &S, Decl *D, + const api_notes::CommonTypeInfo &Info) { + // swift_bridge + if (!Info.getSwiftBridge().empty() && + !D->getAttr()) { + D->addAttr( + SwiftBridgeAttr::CreateImplicit(S.Context, + CopyString(S.Context, + Info.getSwiftBridge()))); + } + + ProcessAPINotes(S, D, static_cast(Info)); +} + /// Process API notes for a variable or property. static void ProcessAPINotes(Sema &S, Decl *D, const api_notes::VariableInfo &Info) { @@ -230,26 +244,31 @@ static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D, static_cast(Info)); } +/// Process API notes for a tag. +static void ProcessAPINotes(Sema &S, TagDecl *D, + const api_notes::TagInfo &Info) { + // Handle common type information. + ProcessAPINotes(S, D, static_cast(Info)); +} + +/// Process API notes for a typedef. +static void ProcessAPINotes(Sema &S, TypedefNameDecl *D, + const api_notes::TypedefInfo &Info) { + // Handle common type information. + ProcessAPINotes(S, D, static_cast(Info)); +} + /// Process API notes for an Objective-C class or protocol. static void ProcessAPINotes(Sema &S, ObjCContainerDecl *D, const api_notes::ObjCContextInfo &Info) { - // Handle common entity information. - ProcessAPINotes(S, D, static_cast(Info)); + // Handle common type information. + ProcessAPINotes(S, D, static_cast(Info)); } /// Process API notes for an Objective-C class. static void ProcessAPINotes(Sema &S, ObjCInterfaceDecl *D, const api_notes::ObjCContextInfo &Info) { - // swift_bridge - if (!Info.getSwiftBridge().empty() && - !D->getAttr()) { - D->addAttr( - SwiftBridgeAttr::CreateImplicit(S.Context, - CopyString(S.Context, - Info.getSwiftBridge()))); - } - // Handle information common to Objective-C classes and protocols. ProcessAPINotes(S, static_cast(D), Info); } @@ -312,6 +331,30 @@ void Sema::ProcessAPINotes(Decl *D) { return; } + // Tags + if (auto Tag = dyn_cast(D)) { + if (api_notes::APINotesReader *Reader + = APINotes.findAPINotes(D->getLocation())) { + if (auto Info = Reader->lookupTag(Tag->getName())) { + ::ProcessAPINotes(*this, Tag, *Info); + } + } + + return; + } + + // Typedefs + if (auto Typedef = dyn_cast(D)) { + if (api_notes::APINotesReader *Reader + = APINotes.findAPINotes(D->getLocation())) { + if (auto Info = Reader->lookupTypedef(Typedef->getName())) { + ::ProcessAPINotes(*this, Typedef, *Info); + } + } + + return; + } + return; } diff --git a/test/APINotes/Inputs/APINotes/HeaderLib.apinotes b/test/APINotes/Inputs/APINotes/HeaderLib.apinotes index a4ddafe2892..8d8ff11f69b 100644 --- a/test/APINotes/Inputs/APINotes/HeaderLib.apinotes +++ b/test/APINotes/Inputs/APINotes/HeaderLib.apinotes @@ -15,3 +15,11 @@ Globals: Nullability: N - Name: unavailable_global_int Availability: none + +Tags: + - Name: unavailable_struct + Availability: none + +Typedefs: + - Name: unavailable_typedef + Availability: none diff --git a/test/APINotes/Inputs/Headers/HeaderLib.h b/test/APINotes/Inputs/Headers/HeaderLib.h index 1cf199cd49a..81a7d63d468 100644 --- a/test/APINotes/Inputs/Headers/HeaderLib.h +++ b/test/APINotes/Inputs/Headers/HeaderLib.h @@ -10,4 +10,7 @@ int unavailable_global_int; void do_something_with_pointers(int *ptr1, int *ptr2); +typedef int unavailable_typedef; +struct unavailable_struct { int x, y, z; }; + #endif diff --git a/test/APINotes/Inputs/roundtrip.apinotes b/test/APINotes/Inputs/roundtrip.apinotes index 18bf2deeb19..378c7258d51 100644 --- a/test/APINotes/Inputs/roundtrip.apinotes +++ b/test/APINotes/Inputs/roundtrip.apinotes @@ -92,3 +92,15 @@ Globals: Availability: available AvailabilityMsg: '' SwiftName: calibratedWhite +Tags: + - Name: NSSomeStruct + Availability: available + AvailabilityMsg: '' + SwiftName: SomeStruct + SwiftBridge: '' +Typedefs: + - Name: NSTypedef + Availability: available + AvailabilityMsg: '' + SwiftName: Typedef + SwiftBridge: '' diff --git a/test/APINotes/availability.m b/test/APINotes/availability.m index 5b996ec8c94..1cfc65862f4 100644 --- a/test/APINotes/availability.m +++ b/test/APINotes/availability.m @@ -12,6 +12,12 @@ int main() { i = unavailable_global_int; // expected-error{{'unavailable_global_int' is unavailable}} // expected-note@HeaderLib.h:9{{'unavailable_global_int' has been explicitly marked unavailable here}} + unavailable_typedef t; // expected-error{{'unavailable_typedef' is unavailable}} + // expected-note@HeaderLib.h:13{{'unavailable_typedef' has been explicitly marked unavailable here}} + + struct unavailable_struct s; // expected-error{{'unavailable_struct' is unavailable}} + // expected-note@HeaderLib.h:14{{'unavailable_struct' has been explicitly marked unavailable here}} + B *b = 0; // expected-error{{'B' is unavailable: just don't}} // expected-note@SomeKit/SomeKit.h:15{{'B' has been explicitly marked unavailable here}} From 4128b494d2a7b19dc15e18defca4efd26a79e784 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 28 Mar 2016 09:31:19 -0700 Subject: [PATCH 325/742] [API Notes] Add support for tags (struct/union/enum/C++ class) and typedefs. Addresses rdar://problem/25365464. --- include/clang/APINotes/APINotesReader.h | 21 ++ include/clang/APINotes/APINotesWriter.h | 12 + include/clang/APINotes/Types.h | 66 +++- lib/APINotes/APINotesFormat.h | 36 ++- lib/APINotes/APINotesReader.cpp | 303 +++++++++++++++++- lib/APINotes/APINotesWriter.cpp | 175 +++++++++- lib/APINotes/APINotesYAMLCompiler.cpp | 216 ++++++++++--- lib/Sema/SemaAPINotes.cpp | 65 +++- .../Inputs/APINotes/HeaderLib.apinotes | 8 + test/APINotes/Inputs/Headers/HeaderLib.h | 3 + test/APINotes/Inputs/roundtrip.apinotes | 12 + test/APINotes/availability.m | 6 + 12 files changed, 834 insertions(+), 89 deletions(-) diff --git a/include/clang/APINotes/APINotesReader.h b/include/clang/APINotes/APINotesReader.h index 4759f85c1ab..16cb5ebe109 100644 --- a/include/clang/APINotes/APINotesReader.h +++ b/include/clang/APINotes/APINotesReader.h @@ -102,6 +102,21 @@ class APINotesReader { /// \returns information about the global function, if known. Optional lookupGlobalFunction(StringRef name); + /// Look for information regarding the given tag + /// (struct/union/enum/C++ class). + /// + /// \param name The name of the tag. + /// + /// \returns information about the tag, if known. + Optional lookupTag(StringRef name); + + /// Look for information regarding the given typedef. + /// + /// \param name The name of the typedef. + /// + /// \returns information about the typedef, if known. + Optional lookupTypedef(StringRef name); + /// Visitor used when walking the contents of the API notes file. class Visitor { public: @@ -131,6 +146,12 @@ class APINotesReader { /// Visit a global function. virtual void visitGlobalFunction(StringRef name, const GlobalFunctionInfo &info); + + /// Visit a tag. + virtual void visitTag(StringRef name, const TagInfo &info); + + /// Visit a typedef. + virtual void visitTypedef(StringRef name, const TypedefInfo &info); }; /// Visit the contents of the API notes file, passing each entity to the diff --git a/include/clang/APINotes/APINotesWriter.h b/include/clang/APINotes/APINotesWriter.h index dca0773aa28..38935a6a271 100644 --- a/include/clang/APINotes/APINotesWriter.h +++ b/include/clang/APINotes/APINotesWriter.h @@ -89,6 +89,18 @@ class APINotesWriter { /// \param name The name of this global function. /// \param info Information about this global function. void addGlobalFunction(StringRef name, const GlobalFunctionInfo &info); + + /// Add information about a tag (struct/union/enum/C++ class). + /// + /// \param name The name of this tag. + /// \param info Information about this tag. + void addTag(StringRef name, const TagInfo &info); + + /// Add information about a typedef. + /// + /// \param name The name of this typedef. + /// \param info Information about this typedef. + void addTypedef(StringRef name, const TypedefInfo &info); }; } // end namespace api_notes diff --git a/include/clang/APINotes/Types.h b/include/clang/APINotes/Types.h index decb137a0f5..5053190f408 100644 --- a/include/clang/APINotes/Types.h +++ b/include/clang/APINotes/Types.h @@ -113,11 +113,43 @@ class CommonEntityInfo { return lhs; } +}; + +/// Describes API notes for types. +class CommonTypeInfo : public CommonEntityInfo { + /// The Swift type to which a given type is bridged. + /// + /// Reflects the swift_bridge attribute. + std::string SwiftBridge; + +public: + CommonTypeInfo() : CommonEntityInfo() { } + + const std::string &getSwiftBridge() const { return SwiftBridge; } + void setSwiftBridge(const std::string &swiftType) { SwiftBridge = swiftType; } + friend CommonTypeInfo &operator|=(CommonTypeInfo &lhs, + const CommonTypeInfo &rhs) { + static_cast(lhs) |= rhs; + if (lhs.SwiftBridge.empty() && !rhs.SwiftBridge.empty()) + lhs.SwiftBridge = rhs.SwiftBridge; + return lhs; + } + + friend bool operator==(const CommonTypeInfo &lhs, + const CommonTypeInfo &rhs) { + return static_cast(lhs) == rhs && + lhs.SwiftBridge == rhs.SwiftBridge; + } + + friend bool operator!=(const CommonTypeInfo &lhs, + const CommonTypeInfo &rhs) { + return !(lhs == rhs); + } }; /// Describes API notes data for an Objective-C class or protocol. -class ObjCContextInfo : public CommonEntityInfo { +class ObjCContextInfo : public CommonTypeInfo { /// Whether this class has a default nullability. unsigned HasDefaultNullability : 1; @@ -127,14 +159,9 @@ class ObjCContextInfo : public CommonEntityInfo { /// Whether this class has designated initializers recorded. unsigned HasDesignatedInits : 1; - /// The Swift type to which a given Objective-C class is bridged. - /// - /// Reflects the swift_bridge attribute. - std::string SwiftBridge; - public: ObjCContextInfo() - : CommonEntityInfo(), + : CommonTypeInfo(), HasDefaultNullability(0), DefaultNullability(0), HasDesignatedInits(0) @@ -167,15 +194,11 @@ class ObjCContextInfo : public CommonEntityInfo { DefaultNullability = 0; } - const std::string &getSwiftBridge() const { return SwiftBridge; } - void setSwiftBridge(const std::string &swiftType) { SwiftBridge = swiftType; } - friend bool operator==(const ObjCContextInfo &lhs, const ObjCContextInfo &rhs) { - return static_cast(lhs) == rhs && + return static_cast(lhs) == rhs && lhs.HasDefaultNullability == rhs.HasDefaultNullability && lhs.DefaultNullability == rhs.DefaultNullability && - lhs.HasDesignatedInits == rhs.HasDesignatedInits && - lhs.SwiftBridge == rhs.SwiftBridge; + lhs.HasDesignatedInits == rhs.HasDesignatedInits; } friend bool operator!=(const ObjCContextInfo &lhs, const ObjCContextInfo &rhs) { @@ -185,7 +208,7 @@ class ObjCContextInfo : public CommonEntityInfo { friend ObjCContextInfo &operator|=(ObjCContextInfo &lhs, const ObjCContextInfo &rhs) { // Merge inherited info. - static_cast(lhs) |= rhs; + static_cast(lhs) |= rhs; // Merge nullability. if (!lhs.getDefaultNullability()) { @@ -196,9 +219,6 @@ class ObjCContextInfo : public CommonEntityInfo { lhs.HasDesignatedInits |= rhs.HasDesignatedInits; - if (lhs.SwiftBridge.empty() && !rhs.SwiftBridge.empty()) - lhs.SwiftBridge = rhs.SwiftBridge; - return lhs; } @@ -436,6 +456,18 @@ class GlobalFunctionInfo : public FunctionInfo { GlobalFunctionInfo() : FunctionInfo() { } }; +/// Describes API notes data for a tag. +class TagInfo : public CommonTypeInfo { +public: + TagInfo() : CommonTypeInfo() { } +}; + +/// Describes API notes data for a typedef. +class TypedefInfo : public CommonTypeInfo { +public: + TypedefInfo() : CommonTypeInfo() { } +}; + } // end namespace api_notes } // end namespace clang diff --git a/lib/APINotes/APINotesFormat.h b/lib/APINotes/APINotesFormat.h index 79241042951..4cdad891841 100644 --- a/lib/APINotes/APINotesFormat.h +++ b/lib/APINotes/APINotesFormat.h @@ -35,7 +35,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 8; +const uint16_t VERSION_MINOR = 9; using IdentifierID = Fixnum<31>; using IdentifierIDField = BCVBR<16>; @@ -84,7 +84,15 @@ enum BlockID { /// The (global) functions data block, which maps global function names to /// information about the global function. - GLOBAL_FUNCTION_BLOCK_ID + GLOBAL_FUNCTION_BLOCK_ID, + + /// The tag data block, which maps tag names to information about + /// the tags. + TAG_BLOCK_ID, + + /// The typedef data block, which maps typedef names to information about + /// the typedefs. + TYPEDEF_BLOCK_ID, }; namespace control_block { @@ -193,6 +201,30 @@ namespace global_function_block { >; } +namespace tag_block { + enum { + TAG_DATA = 1 + }; + + using TagDataLayout = BCRecordLayout< + TAG_DATA, // record ID + BCVBR<16>, // table offset within the blob (see below) + BCBlob // map from name to tag information + >; +}; + +namespace typedef_block { + enum { + TYPEDEF_DATA = 1 + }; + + using TypedefDataLayout = BCRecordLayout< + TYPEDEF_DATA, // record ID + BCVBR<16>, // table offset within the blob (see below) + BCBlob // map from name to typedef information + >; +}; + /// A stored Objective-C selector. struct StoredObjCSelector { unsigned NumPieces; diff --git a/lib/APINotes/APINotesReader.cpp b/lib/APINotes/APINotesReader.cpp index 2d8fcb70690..b4d8f5750e4 100644 --- a/lib/APINotes/APINotesReader.cpp +++ b/lib/APINotes/APINotesReader.cpp @@ -48,6 +48,17 @@ namespace { data += swiftNameLength; } + /// Read serialized CommonTypeInfo. + void readCommonTypeInfo(const uint8_t *&data, CommonTypeInfo &info) { + readCommonEntityInfo(data, info); + + unsigned swiftBridgeLength = + endian::readNext(data); + info.setSwiftBridge( + StringRef(reinterpret_cast(data), swiftBridgeLength)); + data += swiftBridgeLength; + } + /// Used to deserialize the on-disk identifier table. class IdentifierTableInfo { public: @@ -134,19 +145,12 @@ namespace { unsigned length) { data_type result; result.first = endian::readNext(data); - readCommonEntityInfo(data, result.second); + readCommonTypeInfo(data, result.second); if (*data++) { result.second.setDefaultNullability(static_cast(*data)); } ++data; result.second.setHasDesignatedInits(*data++); - - // swift bridge. - unsigned swiftBridgeLength = - endian::readNext(data); - result.second.setSwiftBridge( - StringRef(reinterpret_cast(data), swiftBridgeLength)); - data += swiftBridgeLength; return result; } @@ -410,6 +414,96 @@ namespace { return info; } }; + + /// Used to deserialize the on-disk tag table. + class TagTableInfo { + public: + using internal_key_type = unsigned; // name ID + using external_key_type = internal_key_type; + using data_type = TagInfo; + using hash_value_type = size_t; + using offset_type = unsigned; + + internal_key_type GetInternalKey(external_key_type key) { + return key; + } + + external_key_type GetExternalKey(internal_key_type key) { + return key; + } + + hash_value_type ComputeHash(internal_key_type key) { + return static_cast(llvm::hash_value(key)); + } + + static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { + return lhs == rhs; + } + + static std::pair + ReadKeyDataLength(const uint8_t *&data) { + unsigned keyLength = endian::readNext(data); + unsigned dataLength = endian::readNext(data); + return { keyLength, dataLength }; + } + + static internal_key_type ReadKey(const uint8_t *data, unsigned length) { + auto nameID = endian::readNext(data); + return nameID; + } + + static data_type ReadData(internal_key_type key, const uint8_t *data, + unsigned length) { + TagInfo info; + readCommonTypeInfo(data, info); + return info; + } + }; + + /// Used to deserialize the on-disk typedef table. + class TypedefTableInfo { + public: + using internal_key_type = unsigned; // name ID + using external_key_type = internal_key_type; + using data_type = TypedefInfo; + using hash_value_type = size_t; + using offset_type = unsigned; + + internal_key_type GetInternalKey(external_key_type key) { + return key; + } + + external_key_type GetExternalKey(internal_key_type key) { + return key; + } + + hash_value_type ComputeHash(internal_key_type key) { + return static_cast(llvm::hash_value(key)); + } + + static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { + return lhs == rhs; + } + + static std::pair + ReadKeyDataLength(const uint8_t *&data) { + unsigned keyLength = endian::readNext(data); + unsigned dataLength = endian::readNext(data); + return { keyLength, dataLength }; + } + + static internal_key_type ReadKey(const uint8_t *data, unsigned length) { + auto nameID = endian::readNext(data); + return nameID; + } + + static data_type ReadData(internal_key_type key, const uint8_t *data, + unsigned length) { + TypedefInfo info; + readCommonTypeInfo(data, info); + return info; + } + }; } // end anonymous namespace class APINotesReader::Implementation { @@ -465,6 +559,18 @@ class APINotesReader::Implementation { /// The global function table. std::unique_ptr GlobalFunctionTable; + using SerializedTagTable = + llvm::OnDiskIterableChainedHashTable; + + /// The tag table. + std::unique_ptr TagTable; + + using SerializedTypedefTable = + llvm::OnDiskIterableChainedHashTable; + + /// The typedef table. + std::unique_ptr TypedefTable; + /// Retrieve the identifier ID for the given string, or an empty /// optional if the string is unknown. Optional getIdentifier(StringRef str); @@ -489,6 +595,10 @@ class APINotesReader::Implementation { SmallVectorImpl &scratch); bool readGlobalFunctionBlock(llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch); + bool readTagBlock(llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch); + bool readTypedefBlock(llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch); }; Optional APINotesReader::Implementation::getIdentifier( @@ -959,6 +1069,112 @@ bool APINotesReader::Implementation::readGlobalFunctionBlock( return false; } +bool APINotesReader::Implementation::readTagBlock( + llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch) { + if (cursor.EnterSubBlock(TAG_BLOCK_ID)) + return true; + + auto next = cursor.advance(); + while (next.Kind != llvm::BitstreamEntry::EndBlock) { + if (next.Kind == llvm::BitstreamEntry::Error) + return true; + + if (next.Kind == llvm::BitstreamEntry::SubBlock) { + // Unknown sub-block, possibly for use by a future version of the + // API notes format. + if (cursor.SkipBlock()) + return true; + + next = cursor.advance(); + continue; + } + + scratch.clear(); + StringRef blobData; + unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + switch (kind) { + case tag_block::TAG_DATA: { + // Already saw tag table. + if (TagTable) + return true; + + uint32_t tableOffset; + tag_block::TagDataLayout::readRecord(scratch, tableOffset); + auto base = reinterpret_cast(blobData.data()); + + TagTable.reset( + SerializedTagTable::Create(base + tableOffset, + base + sizeof(uint32_t), + base)); + break; + } + + default: + // Unknown record, possibly for use by a future version of the + // module format. + break; + } + + next = cursor.advance(); + } + + return false; +} + +bool APINotesReader::Implementation::readTypedefBlock( + llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch) { + if (cursor.EnterSubBlock(TYPEDEF_BLOCK_ID)) + return true; + + auto next = cursor.advance(); + while (next.Kind != llvm::BitstreamEntry::EndBlock) { + if (next.Kind == llvm::BitstreamEntry::Error) + return true; + + if (next.Kind == llvm::BitstreamEntry::SubBlock) { + // Unknown sub-block, possibly for use by a future version of the + // API notes format. + if (cursor.SkipBlock()) + return true; + + next = cursor.advance(); + continue; + } + + scratch.clear(); + StringRef blobData; + unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + switch (kind) { + case typedef_block::TYPEDEF_DATA: { + // Already saw typedef table. + if (TypedefTable) + return true; + + uint32_t tableOffset; + typedef_block::TypedefDataLayout::readRecord(scratch, tableOffset); + auto base = reinterpret_cast(blobData.data()); + + TypedefTable.reset( + SerializedTypedefTable::Create(base + tableOffset, + base + sizeof(uint32_t), + base)); + break; + } + + default: + // Unknown record, possibly for use by a future version of the + // module format. + break; + } + + next = cursor.advance(); + } + + return false; +} + APINotesReader::APINotesReader(std::unique_ptr inputBuffer, bool &failed) : Impl(*new Implementation) @@ -1057,6 +1273,20 @@ APINotesReader::APINotesReader(std::unique_ptr inputBuffer, } break; + case TAG_BLOCK_ID: + if (!hasValidControlBlock || Impl.readTagBlock(cursor, scratch)) { + failed = true; + return; + } + break; + + case TYPEDEF_BLOCK_ID: + if (!hasValidControlBlock || Impl.readTypedefBlock(cursor, scratch)) { + failed = true; + return; + } + break; + default: // Unknown top-level block, possibly for use by a future version of the // module format. @@ -1197,6 +1427,37 @@ Optional APINotesReader::lookupGlobalFunction( return *known; } + +Optional APINotesReader::lookupTag(StringRef name) { + if (!Impl.TagTable) + return None; + + Optional nameID = Impl.getIdentifier(name); + if (!nameID) + return None; + + auto known = Impl.TagTable->find(*nameID); + if (known == Impl.TagTable->end()) + return None; + + return *known; +} + +Optional APINotesReader::lookupTypedef(StringRef name) { + if (!Impl.TypedefTable) + return None; + + Optional nameID = Impl.getIdentifier(name); + if (!nameID) + return None; + + auto known = Impl.TypedefTable->find(*nameID); + if (known == Impl.TypedefTable->end()) + return None; + + return *known; +} + APINotesReader::Visitor::~Visitor() { } void APINotesReader::Visitor::visitObjCClass(ContextID contextID, @@ -1224,6 +1485,14 @@ void APINotesReader::Visitor::visitGlobalFunction( StringRef name, const GlobalFunctionInfo &info) { } +void APINotesReader::Visitor::visitTag( + StringRef name, + const TagInfo &info) { } + +void APINotesReader::Visitor::visitTypedef( + StringRef name, + const TypedefInfo &info) { } + void APINotesReader::visit(Visitor &visitor) { // FIXME: All of these iterations would be significantly more efficient if we // could get the keys and data together, but OnDiskIterableHashTable doesn't @@ -1310,5 +1579,23 @@ void APINotesReader::visit(Visitor &visitor) { visitor.visitGlobalVariable(name, info); } } + + // Visit tags. + if (Impl.TagTable) { + for (auto key : Impl.TagTable->keys()) { + auto name = identifiers[key]; + auto info = *Impl.TagTable->find(key); + visitor.visitTag(name, info); + } + } + + // Visit typedefs. + if (Impl.TypedefTable) { + for (auto key : Impl.TypedefTable->keys()) { + auto name = identifiers[key]; + auto info = *Impl.TypedefTable->find(key); + visitor.visitTypedef(name, info); + } + } } diff --git a/lib/APINotes/APINotesWriter.cpp b/lib/APINotes/APINotesWriter.cpp index 102932fdad0..bc17773ed60 100644 --- a/lib/APINotes/APINotesWriter.cpp +++ b/lib/APINotes/APINotesWriter.cpp @@ -77,6 +77,16 @@ class APINotesWriter::Implementation { /// Indexed by the identifier ID. llvm::DenseMap GlobalFunctions; + /// Information about tags. + /// + /// Indexed by the identifier ID. + llvm::DenseMap Tags; + + /// Information about typedefs. + /// + /// Indexed by the identifier ID. + llvm::DenseMap Typedefs; + /// Retrieve the ID for the given identifier. IdentifierID getIdentifier(StringRef identifier) { if (identifier.empty()) @@ -123,6 +133,8 @@ class APINotesWriter::Implementation { void writeObjCSelectorBlock(llvm::BitstreamWriter &writer); void writeGlobalVariableBlock(llvm::BitstreamWriter &writer); void writeGlobalFunctionBlock(llvm::BitstreamWriter &writer); + void writeTagBlock(llvm::BitstreamWriter &writer); + void writeTypedefBlock(llvm::BitstreamWriter &writer); }; /// Record the name of a block. @@ -279,6 +291,20 @@ namespace { out.write(info.SwiftName.c_str(), info.SwiftName.size()); } + // Retrieve the serialized size of the given CommonTypeInfo, for use + // in on-disk hash tables. + static unsigned getCommonTypeInfoSize(const CommonTypeInfo &info) { + return 2 + info.getSwiftBridge().size() + getCommonEntityInfoSize(info); + } + + /// Emit a serialized representation of the common type information. + static void emitCommonTypeInfo(raw_ostream &out, const CommonTypeInfo &info) { + emitCommonEntityInfo(out, info); + endian::Writer writer(out); + writer.write(info.getSwiftBridge().size()); + out.write(info.getSwiftBridge().c_str(), info.getSwiftBridge().size()); + } + /// Used to serialize the on-disk Objective-C context table. class ObjCContextTableInfo { public: @@ -301,9 +327,8 @@ namespace { data_type_ref data) { uint32_t keyLength = sizeof(IdentifierID) + 1; uint32_t dataLength = sizeof(ContextID) - + getCommonEntityInfoSize(data.second) - + dataBytes - + 2 + data.second.getSwiftBridge().size(); + + getCommonTypeInfoSize(data.second) + + dataBytes; endian::Writer writer(out); writer.write(keyLength); writer.write(dataLength); @@ -321,7 +346,7 @@ namespace { endian::Writer writer(out); writer.write(data.first); - emitCommonEntityInfo(out, data.second); + emitCommonTypeInfo(out, data.second); // FIXME: Inefficient representation. uint8_t bytes[dataBytes] = { 0, 0, 0 }; @@ -334,10 +359,6 @@ namespace { bytes[2] = data.second.hasDesignatedInits(); out.write(reinterpret_cast(bytes), dataBytes); - - writer.write(data.second.getSwiftBridge().size()); - out.write(data.second.getSwiftBridge().data(), - data.second.getSwiftBridge().size()); } }; } // end anonymous namespace @@ -733,6 +754,130 @@ void APINotesWriter::Implementation::writeGlobalFunctionBlock( layout.emit(ScratchRecord, tableOffset, hashTableBlob); } +namespace { + /// Used to serialize the on-disk tag table. + class TagTableInfo { + public: + using key_type = unsigned; // name ID + using key_type_ref = key_type; + using data_type = TagInfo; + using data_type_ref = const data_type &; + using hash_value_type = size_t; + using offset_type = unsigned; + + hash_value_type ComputeHash(key_type_ref key) { + return static_cast(llvm::hash_value(key)); + } + + std::pair EmitKeyDataLength(raw_ostream &out, + key_type_ref key, + data_type_ref data) { + uint32_t keyLength = sizeof(IdentifierID); + uint32_t dataLength = getCommonTypeInfoSize(data); + endian::Writer writer(out); + writer.write(keyLength); + writer.write(dataLength); + return { keyLength, dataLength }; + } + + void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { + endian::Writer writer(out); + writer.write(key); + } + + void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, + unsigned len) { + emitCommonTypeInfo(out, data); + } + }; +} // end anonymous namespace + +void APINotesWriter::Implementation::writeTagBlock( + llvm::BitstreamWriter &writer) { + BCBlockRAII restoreBlock(writer, TAG_BLOCK_ID, 3); + + if (Tags.empty()) + return; + + llvm::SmallString<4096> hashTableBlob; + uint32_t tableOffset; + { + llvm::OnDiskChainedHashTableGenerator generator; + for (auto &entry : Tags) + generator.insert(entry.first, entry.second); + + llvm::raw_svector_ostream blobStream(hashTableBlob); + // Make sure that no bucket is at offset 0 + endian::Writer(blobStream).write(0); + tableOffset = generator.Emit(blobStream); + } + + tag_block::TagDataLayout layout(writer); + layout.emit(ScratchRecord, tableOffset, hashTableBlob); +} + +namespace { + /// Used to serialize the on-disk typedef table. + class TypedefTableInfo { + public: + using key_type = unsigned; // name ID + using key_type_ref = key_type; + using data_type = TypedefInfo; + using data_type_ref = const data_type &; + using hash_value_type = size_t; + using offset_type = unsigned; + + hash_value_type ComputeHash(key_type_ref key) { + return static_cast(llvm::hash_value(key)); + } + + std::pair EmitKeyDataLength(raw_ostream &out, + key_type_ref key, + data_type_ref data) { + uint32_t keyLength = sizeof(IdentifierID); + uint32_t dataLength = getCommonTypeInfoSize(data); + endian::Writer writer(out); + writer.write(keyLength); + writer.write(dataLength); + return { keyLength, dataLength }; + } + + void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { + endian::Writer writer(out); + writer.write(key); + } + + void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, + unsigned len) { + emitCommonTypeInfo(out, data); + } + }; +} // end anonymous namespace + +void APINotesWriter::Implementation::writeTypedefBlock( + llvm::BitstreamWriter &writer) { + BCBlockRAII restoreBlock(writer, TYPEDEF_BLOCK_ID, 3); + + if (Typedefs.empty()) + return; + + llvm::SmallString<4096> hashTableBlob; + uint32_t tableOffset; + { + llvm::OnDiskChainedHashTableGenerator generator; + for (auto &entry : Typedefs) + generator.insert(entry.first, entry.second); + + llvm::raw_svector_ostream blobStream(hashTableBlob); + // Make sure that no bucket is at offset 0 + endian::Writer(blobStream).write(0); + tableOffset = generator.Emit(blobStream); + } + + typedef_block::TypedefDataLayout layout(writer); + layout.emit(ScratchRecord, tableOffset, hashTableBlob); +} + void APINotesWriter::Implementation::writeToStream(llvm::raw_ostream &os) { // Write the API notes file into a buffer. SmallVector buffer; @@ -753,6 +898,8 @@ void APINotesWriter::Implementation::writeToStream(llvm::raw_ostream &os) { writeObjCSelectorBlock(writer); writeGlobalVariableBlock(writer); writeGlobalFunctionBlock(writer); + writeTagBlock(writer); + writeTypedefBlock(writer); } // Write the buffer to the stream. @@ -856,3 +1003,15 @@ void APINotesWriter::addGlobalFunction(llvm::StringRef name, assert(!Impl.GlobalFunctions.count(nameID)); Impl.GlobalFunctions[nameID] = info; } + +void APINotesWriter::addTag(llvm::StringRef name, const TagInfo &info) { + IdentifierID tagID = Impl.getIdentifier(name); + assert(!Impl.Tags.count(tagID)); + Impl.Tags[tagID] = info; +} + +void APINotesWriter::addTypedef(llvm::StringRef name, const TypedefInfo &info) { + IdentifierID typedefID = Impl.getIdentifier(name); + assert(!Impl.Typedefs.count(typedefID)); + Impl.Typedefs[typedefID] = info; +} diff --git a/lib/APINotes/APINotesYAMLCompiler.cpp b/lib/APINotes/APINotesYAMLCompiler.cpp index 17d8fab3637..39f6c9d6410 100644 --- a/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/lib/APINotes/APINotesYAMLCompiler.cpp @@ -57,6 +57,10 @@ ... Globals: # List of globals ... + Tags: # List of tags (struct/union/enum/C++ class) + ... + Typedefs: # List of typedef-names and C++11 type aliases + ... Each class and protocol is defined as following: @@ -212,6 +216,22 @@ namespace { }; typedef std::vector GlobalVariablesSeq; + struct Tag { + StringRef Name; + AvailabilityItem Availability; + StringRef SwiftName; + StringRef SwiftBridge; + }; + typedef std::vector TagsSeq; + + struct Typedef { + StringRef Name; + AvailabilityItem Availability; + StringRef SwiftName; + StringRef SwiftBridge; + }; + typedef std::vector TypedefsSeq; + struct Module { StringRef Name; AvailabilityItem Availability; @@ -219,6 +239,8 @@ namespace { ClassesSeq Protocols; FunctionsSeq Functions; GlobalVariablesSeq Globals; + TagsSeq Tags; + TypedefsSeq Typedefs; LLVM_ATTRIBUTE_DEPRECATED( void dump() LLVM_ATTRIBUTE_USED, @@ -232,6 +254,8 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(Property) LLVM_YAML_IS_SEQUENCE_VECTOR(Class) LLVM_YAML_IS_SEQUENCE_VECTOR(Function) LLVM_YAML_IS_SEQUENCE_VECTOR(GlobalVariable) +LLVM_YAML_IS_SEQUENCE_VECTOR(Tag) +LLVM_YAML_IS_SEQUENCE_VECTOR(Typedef) namespace llvm { namespace yaml { @@ -345,6 +369,28 @@ namespace llvm { } }; + template <> + struct MappingTraits { + static void mapping(IO &io, Tag& t) { + io.mapRequired("Name", t.Name); + io.mapOptional("Availability", t.Availability.Mode); + io.mapOptional("AvailabilityMsg", t.Availability.Msg); + io.mapOptional("SwiftName", t.SwiftName); + io.mapOptional("SwiftBridge", t.SwiftBridge); + } + }; + + template <> + struct MappingTraits { + static void mapping(IO &io, Typedef& t) { + io.mapRequired("Name", t.Name); + io.mapOptional("Availability", t.Availability.Mode); + io.mapOptional("AvailabilityMsg", t.Availability.Msg); + io.mapOptional("SwiftName", t.SwiftName); + io.mapOptional("SwiftBridge", t.SwiftBridge); + } + }; + template <> struct MappingTraits { static void mapping(IO &io, Module& m) { @@ -355,6 +401,8 @@ namespace llvm { io.mapOptional("Protocols", m.Protocols); io.mapOptional("Functions", m.Functions); io.mapOptional("Globals", m.Globals); + io.mapOptional("Tags", m.Tags); + io.mapOptional("Typedefs", m.Typedefs); } }; } @@ -377,11 +425,7 @@ static bool parseAPINotes(StringRef yamlInput, Module &module, return static_cast(yin.error()); } -static bool compile(const Module &module, - llvm::raw_ostream &os, - api_notes::OSType targetOS, - llvm::SourceMgr::DiagHandlerTy diagHandler, - void *diagHandlerCtxt){ +namespace { using namespace api_notes; class YAMLConverter { @@ -431,7 +475,7 @@ static bool compile(const Module &module, outInfo.UnavailableMsg = in.Msg; } else { if (!in.Msg.empty()) { - emitError("availability message for available class '" + + emitError("availability message for available API '" + apiName + "' will not be used"); } } @@ -466,17 +510,37 @@ static bool compile(const Module &module, } } + /// Convert the common parts of an entity from YAML. + template + bool convertCommon(const T& common, CommonEntityInfo &info, + StringRef apiName) { + if (!isAvailable(common.Availability)) + return true; + + convertAvailability(common.Availability, info, apiName); + info.SwiftName = common.SwiftName; + return false; + } + + /// Convert the common parts of a type entity from YAML. + template + bool convertCommonType(const T& common, CommonTypeInfo &info, + StringRef apiName) { + if (convertCommon(common, info, apiName)) + return true; + + info.setSwiftBridge(common.SwiftBridge); + return false; + } + // Translate from Method into ObjCMethodInfo and write it out. void convertMethod(const Method &meth, ContextID classID, StringRef className) { ObjCMethodInfo mInfo; - if (!isAvailable(meth.Availability)) + if (convertCommon(meth, mInfo, meth.Selector)) return; - convertAvailability(meth.Availability, mInfo, meth.Selector); - mInfo.SwiftName = meth.SwiftName; - // Check if the selector ends with ':' to determine if it takes arguments. bool takesArguments = meth.Selector.endswith(":"); @@ -505,27 +569,20 @@ static bool compile(const Module &module, // Write it. Writer->addObjCMethod(classID, selectorRef, - meth.Kind == MethodKind::Instance, - mInfo); + meth.Kind == MethodKind::Instance, + mInfo); } void convertContext(const Class &cl, bool isClass) { // Write the class. ObjCContextInfo cInfo; - // First, translate and check availability info. - if (!isAvailable(cl.Availability)) + if (convertCommonType(cl, cInfo, cl.Name)) return; - convertAvailability(cl.Availability, cInfo, cl.Name); - cInfo.SwiftName = cl.SwiftName; - if (cl.AuditedForNullability) cInfo.setDefaultNullability(*DefaultNullability); - if (isClass) - cInfo.setSwiftBridge(cl.SwiftBridge); - ContextID clID = isClass ? Writer->addObjCClass(cl.Name, cInfo) : Writer->addObjCProtocol(cl.Name, cInfo); @@ -644,12 +701,52 @@ static bool compile(const Module &module, Writer->addGlobalFunction(function.Name, info); } + // Write all tags. + llvm::StringSet<> knownTags; + for (const auto &t : TheModule.Tags) { + // Check for duplicate tag definitions. + if (!knownTags.insert(t.Name).second) { + emitError("multiple definitions of tag '" + t.Name + "'"); + continue; + } + + TagInfo tagInfo; + if (convertCommonType(t, tagInfo, t.Name)) + continue; + + Writer->addTag(t.Name, tagInfo); + } + + // Write all typedefs. + llvm::StringSet<> knownTypedefs; + for (const auto &t : TheModule.Typedefs) { + // Check for duplicate typedef definitions. + if (!knownTags.insert(t.Name).second) { + emitError("multiple definitions of typedef '" + t.Name + "'"); + continue; + } + + TypedefInfo typedefInfo; + if (convertCommonType(t, typedefInfo, t.Name)) + continue; + + Writer->addTypedef(t.Name, typedefInfo); + } + if (!ErrorOccured) Writer->writeToStream(OS); return ErrorOccured; } }; +} + +static bool compile(const Module &module, + llvm::raw_ostream &os, + api_notes::OSType targetOS, + llvm::SourceMgr::DiagHandlerTy diagHandler, + void *diagHandlerCtxt){ + using namespace api_notes; YAMLConverter c(module, targetOS, os, diagHandler, diagHandlerCtxt); return c.convertModule(); @@ -689,15 +786,7 @@ bool api_notes::compileAPINotes(StringRef yamlInput, return compile(module, os, targetOS, diagHandler, diagHandlerCtxt); } -bool api_notes::decompileAPINotes(std::unique_ptr input, - llvm::raw_ostream &os) { - // Try to read the file. - auto reader = APINotesReader::get(std::move(input)); - if (!reader) { - llvm::errs() << "not a well-formed API notes binary file\n"; - return true; - } - +namespace { // Deserialize the API notes file into a module. class DecompileVisitor : public APINotesReader::Visitor { /// Allocator used to clone those strings that need it. @@ -720,20 +809,28 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, return StringRef(reinterpret_cast(ptr), string.size()); } + template + void handleCommon(T &record, const CommonEntityInfo &info) { + handleAvailability(record.Availability, info); + record.SwiftName = copyString(info.SwiftName); + } + + template + void handleCommonType(T &record, const CommonTypeInfo &info) { + handleCommon(record, info); + record.SwiftBridge = copyString(info.getSwiftBridge()); + } + /// Map Objective-C context info. void handleObjCContext(Class &record, StringRef name, const ObjCContextInfo &info) { record.Name = name; - // Handle class information. - handleAvailability(record.Availability, info); - record.SwiftName = copyString(info.SwiftName); + handleCommonType(record, info); if (info.getDefaultNullability()) { record.AuditedForNullability = true; } - - record.SwiftBridge = copyString(info.getSwiftBridge()); } /// Map availability information, if present. @@ -792,10 +889,9 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, method.Selector = copyString(selector); method.Kind = isInstanceMethod ? MethodKind::Instance : MethodKind::Class; + handleCommon(method, info); handleNullability(method.Nullability, method.NullabilityOfRet, info, selector.count(':')); - handleAvailability(method.Availability, info); - method.SwiftName = copyString(info.SwiftName); method.FactoryAsInit = info.getFactoryAsInitKind(); method.DesignatedInit = info.DesignatedInit; method.Required = info.Required; @@ -811,8 +907,7 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, const ObjCPropertyInfo &info) { Property property; property.Name = name; - handleAvailability(property.Availability, info); - property.SwiftName = copyString(info.SwiftName); + handleCommon(property, info); // FIXME: No way to represent "not audited for nullability". if (auto nullability = info.getNullability()) { @@ -830,8 +925,7 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, const GlobalFunctionInfo &info) { Function function; function.Name = name; - handleAvailability(function.Availability, info); - function.SwiftName = copyString(info.SwiftName); + handleCommon(function, info); if (info.NumAdjustedNullable > 0) handleNullability(function.Nullability, function.NullabilityOfRet, info, info.NumAdjustedNullable-1); @@ -843,8 +937,7 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, const GlobalVariableInfo &info) { GlobalVariable global; global.Name = name; - handleAvailability(global.Availability, info); - global.SwiftName = copyString(info.SwiftName); + handleCommon(global, info); // FIXME: No way to represent "not audited for nullability". if (auto nullability = info.getNullability()) { @@ -854,10 +947,35 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, TheModule.Globals.push_back(global); } + virtual void visitTag(StringRef name, const TagInfo &info) { + Tag tag; + tag.Name = name; + handleCommonType(tag, info); + TheModule.Tags.push_back(tag); + } + + virtual void visitTypedef(StringRef name, const TypedefInfo &info) { + Typedef td; + td.Name = name; + handleCommonType(td, info); + TheModule.Typedefs.push_back(td); + } + /// Retrieve the module. Module &getModule() { return TheModule; } - } decompileVisitor; + }; +} + +bool api_notes::decompileAPINotes(std::unique_ptr input, + llvm::raw_ostream &os) { + // Try to read the file. + auto reader = APINotesReader::get(std::move(input)); + if (!reader) { + llvm::errs() << "not a well-formed API notes binary file\n"; + return true; + } + DecompileVisitor decompileVisitor; reader->visit(decompileVisitor); // Sort the data in the module, because the API notes reader doesn't preserve @@ -911,6 +1029,18 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, return lhs.Name < rhs.Name; }); + // Sort tags. + std::sort(module.Tags.begin(), module.Tags.end(), + [](const Tag &lhs, const Tag &rhs) -> bool { + return lhs.Name < rhs.Name; + }); + + // Sort typedefs. + std::sort(module.Typedefs.begin(), module.Typedefs.end(), + [](const Typedef &lhs, const Typedef &rhs) -> bool { + return lhs.Name < rhs.Name; + }); + // Output the YAML representation. Output yout(os); yout << module; diff --git a/lib/Sema/SemaAPINotes.cpp b/lib/Sema/SemaAPINotes.cpp index 6e06e4cc80a..fd70e94cd32 100644 --- a/lib/Sema/SemaAPINotes.cpp +++ b/lib/Sema/SemaAPINotes.cpp @@ -130,6 +130,20 @@ static void ProcessAPINotes(Sema &S, Decl *D, } } +static void ProcessAPINotes(Sema &S, Decl *D, + const api_notes::CommonTypeInfo &Info) { + // swift_bridge + if (!Info.getSwiftBridge().empty() && + !D->getAttr()) { + D->addAttr( + SwiftBridgeAttr::CreateImplicit(S.Context, + CopyString(S.Context, + Info.getSwiftBridge()))); + } + + ProcessAPINotes(S, D, static_cast(Info)); +} + /// Process API notes for a variable or property. static void ProcessAPINotes(Sema &S, Decl *D, const api_notes::VariableInfo &Info) { @@ -232,26 +246,31 @@ static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D, static_cast(Info)); } +/// Process API notes for a tag. +static void ProcessAPINotes(Sema &S, TagDecl *D, + const api_notes::TagInfo &Info) { + // Handle common type information. + ProcessAPINotes(S, D, static_cast(Info)); +} + +/// Process API notes for a typedef. +static void ProcessAPINotes(Sema &S, TypedefNameDecl *D, + const api_notes::TypedefInfo &Info) { + // Handle common type information. + ProcessAPINotes(S, D, static_cast(Info)); +} + /// Process API notes for an Objective-C class or protocol. static void ProcessAPINotes(Sema &S, ObjCContainerDecl *D, const api_notes::ObjCContextInfo &Info) { - // Handle common entity information. - ProcessAPINotes(S, D, static_cast(Info)); + // Handle common type information. + ProcessAPINotes(S, D, static_cast(Info)); } /// Process API notes for an Objective-C class. static void ProcessAPINotes(Sema &S, ObjCInterfaceDecl *D, const api_notes::ObjCContextInfo &Info) { - // swift_bridge - if (!Info.getSwiftBridge().empty() && - !D->getAttr()) { - D->addAttr( - SwiftBridgeAttr::CreateImplicit(S.Context, - CopyString(S.Context, - Info.getSwiftBridge()))); - } - // Handle information common to Objective-C classes and protocols. ProcessAPINotes(S, static_cast(D), Info); } @@ -314,6 +333,30 @@ void Sema::ProcessAPINotes(Decl *D) { return; } + // Tags + if (auto Tag = dyn_cast(D)) { + if (api_notes::APINotesReader *Reader + = APINotes.findAPINotes(D->getLocation())) { + if (auto Info = Reader->lookupTag(Tag->getName())) { + ::ProcessAPINotes(*this, Tag, *Info); + } + } + + return; + } + + // Typedefs + if (auto Typedef = dyn_cast(D)) { + if (api_notes::APINotesReader *Reader + = APINotes.findAPINotes(D->getLocation())) { + if (auto Info = Reader->lookupTypedef(Typedef->getName())) { + ::ProcessAPINotes(*this, Typedef, *Info); + } + } + + return; + } + return; } diff --git a/test/APINotes/Inputs/APINotes/HeaderLib.apinotes b/test/APINotes/Inputs/APINotes/HeaderLib.apinotes index a4ddafe2892..8d8ff11f69b 100644 --- a/test/APINotes/Inputs/APINotes/HeaderLib.apinotes +++ b/test/APINotes/Inputs/APINotes/HeaderLib.apinotes @@ -15,3 +15,11 @@ Globals: Nullability: N - Name: unavailable_global_int Availability: none + +Tags: + - Name: unavailable_struct + Availability: none + +Typedefs: + - Name: unavailable_typedef + Availability: none diff --git a/test/APINotes/Inputs/Headers/HeaderLib.h b/test/APINotes/Inputs/Headers/HeaderLib.h index 1cf199cd49a..81a7d63d468 100644 --- a/test/APINotes/Inputs/Headers/HeaderLib.h +++ b/test/APINotes/Inputs/Headers/HeaderLib.h @@ -10,4 +10,7 @@ int unavailable_global_int; void do_something_with_pointers(int *ptr1, int *ptr2); +typedef int unavailable_typedef; +struct unavailable_struct { int x, y, z; }; + #endif diff --git a/test/APINotes/Inputs/roundtrip.apinotes b/test/APINotes/Inputs/roundtrip.apinotes index 18bf2deeb19..378c7258d51 100644 --- a/test/APINotes/Inputs/roundtrip.apinotes +++ b/test/APINotes/Inputs/roundtrip.apinotes @@ -92,3 +92,15 @@ Globals: Availability: available AvailabilityMsg: '' SwiftName: calibratedWhite +Tags: + - Name: NSSomeStruct + Availability: available + AvailabilityMsg: '' + SwiftName: SomeStruct + SwiftBridge: '' +Typedefs: + - Name: NSTypedef + Availability: available + AvailabilityMsg: '' + SwiftName: Typedef + SwiftBridge: '' diff --git a/test/APINotes/availability.m b/test/APINotes/availability.m index 5b996ec8c94..1cfc65862f4 100644 --- a/test/APINotes/availability.m +++ b/test/APINotes/availability.m @@ -12,6 +12,12 @@ int main() { i = unavailable_global_int; // expected-error{{'unavailable_global_int' is unavailable}} // expected-note@HeaderLib.h:9{{'unavailable_global_int' has been explicitly marked unavailable here}} + unavailable_typedef t; // expected-error{{'unavailable_typedef' is unavailable}} + // expected-note@HeaderLib.h:13{{'unavailable_typedef' has been explicitly marked unavailable here}} + + struct unavailable_struct s; // expected-error{{'unavailable_struct' is unavailable}} + // expected-note@HeaderLib.h:14{{'unavailable_struct' has been explicitly marked unavailable here}} + B *b = 0; // expected-error{{'B' is unavailable: just don't}} // expected-note@SomeKit/SomeKit.h:15{{'B' has been explicitly marked unavailable here}} From 490102f0fae24f465227f643b78e45253d904a1f Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 28 Mar 2016 10:45:01 -0700 Subject: [PATCH 326/742] [API Notes] Add support for enumerators. Addresses the rest of rdar://problem/25365464. --- include/clang/APINotes/APINotesReader.h | 11 ++ include/clang/APINotes/APINotesWriter.h | 6 + include/clang/APINotes/Types.h | 6 + lib/APINotes/APINotesFormat.h | 18 ++- lib/APINotes/APINotesReader.cpp | 143 ++++++++++++++++++++++++ lib/APINotes/APINotesWriter.cpp | 76 +++++++++++++ lib/APINotes/APINotesYAMLCompiler.cpp | 53 +++++++++ lib/Sema/SemaAPINotes.cpp | 23 ++++ test/APINotes/Inputs/roundtrip.apinotes | 5 + 9 files changed, 340 insertions(+), 1 deletion(-) diff --git a/include/clang/APINotes/APINotesReader.h b/include/clang/APINotes/APINotesReader.h index 16cb5ebe109..839e1dc5ee9 100644 --- a/include/clang/APINotes/APINotesReader.h +++ b/include/clang/APINotes/APINotesReader.h @@ -102,6 +102,13 @@ class APINotesReader { /// \returns information about the global function, if known. Optional lookupGlobalFunction(StringRef name); + /// Look for information regarding the given enumerator. + /// + /// \param name The name of the enumerator. + /// + /// \returns information about the enumerator, if known. + Optional lookupEnumConstant(StringRef name); + /// Look for information regarding the given tag /// (struct/union/enum/C++ class). /// @@ -147,6 +154,10 @@ class APINotesReader { virtual void visitGlobalFunction(StringRef name, const GlobalFunctionInfo &info); + /// Visit an enumerator. + virtual void visitEnumConstant(StringRef name, + const EnumConstantInfo &info); + /// Visit a tag. virtual void visitTag(StringRef name, const TagInfo &info); diff --git a/include/clang/APINotes/APINotesWriter.h b/include/clang/APINotes/APINotesWriter.h index 38935a6a271..66dc8ffebba 100644 --- a/include/clang/APINotes/APINotesWriter.h +++ b/include/clang/APINotes/APINotesWriter.h @@ -90,6 +90,12 @@ class APINotesWriter { /// \param info Information about this global function. void addGlobalFunction(StringRef name, const GlobalFunctionInfo &info); + /// Add information about an enumerator. + /// + /// \param name The name of this enumerator. + /// \param info Information about this enumerator. + void addEnumConstant(StringRef name, const EnumConstantInfo &info); + /// Add information about a tag (struct/union/enum/C++ class). /// /// \param name The name of this tag. diff --git a/include/clang/APINotes/Types.h b/include/clang/APINotes/Types.h index 5053190f408..2fc4ee8fe73 100644 --- a/include/clang/APINotes/Types.h +++ b/include/clang/APINotes/Types.h @@ -456,6 +456,12 @@ class GlobalFunctionInfo : public FunctionInfo { GlobalFunctionInfo() : FunctionInfo() { } }; +/// Describes API notes data for an enumerator. +class EnumConstantInfo : public CommonEntityInfo { +public: + EnumConstantInfo() : CommonEntityInfo() { } +}; + /// Describes API notes data for a tag. class TagInfo : public CommonTypeInfo { public: diff --git a/lib/APINotes/APINotesFormat.h b/lib/APINotes/APINotesFormat.h index 4cdad891841..d344a30a661 100644 --- a/lib/APINotes/APINotesFormat.h +++ b/lib/APINotes/APINotesFormat.h @@ -35,7 +35,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 9; +const uint16_t VERSION_MINOR = 10; // enum constants using IdentifierID = Fixnum<31>; using IdentifierIDField = BCVBR<16>; @@ -93,6 +93,10 @@ enum BlockID { /// The typedef data block, which maps typedef names to information about /// the typedefs. TYPEDEF_BLOCK_ID, + + /// The enum constant data block, which maps enumerator names to + /// information about the enumerators. + ENUM_CONSTANT_BLOCK_ID, }; namespace control_block { @@ -225,6 +229,18 @@ namespace typedef_block { >; }; +namespace enum_constant_block { + enum { + ENUM_CONSTANT_DATA = 1 + }; + + using EnumConstantDataLayout = BCRecordLayout< + ENUM_CONSTANT_DATA, // record ID + BCVBR<16>, // table offset within the blob (see below) + BCBlob // map from name to enumerator information + >; +} + /// A stored Objective-C selector. struct StoredObjCSelector { unsigned NumPieces; diff --git a/lib/APINotes/APINotesReader.cpp b/lib/APINotes/APINotesReader.cpp index b4d8f5750e4..62acecf3cc8 100644 --- a/lib/APINotes/APINotesReader.cpp +++ b/lib/APINotes/APINotesReader.cpp @@ -415,6 +415,51 @@ namespace { } }; + /// Used to deserialize the on-disk enumerator table. + class EnumConstantTableInfo { + public: + using internal_key_type = unsigned; // name ID + using external_key_type = internal_key_type; + using data_type = EnumConstantInfo; + using hash_value_type = size_t; + using offset_type = unsigned; + + internal_key_type GetInternalKey(external_key_type key) { + return key; + } + + external_key_type GetExternalKey(internal_key_type key) { + return key; + } + + hash_value_type ComputeHash(internal_key_type key) { + return static_cast(llvm::hash_value(key)); + } + + static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { + return lhs == rhs; + } + + static std::pair + ReadKeyDataLength(const uint8_t *&data) { + unsigned keyLength = endian::readNext(data); + unsigned dataLength = endian::readNext(data); + return { keyLength, dataLength }; + } + + static internal_key_type ReadKey(const uint8_t *data, unsigned length) { + auto nameID = endian::readNext(data); + return nameID; + } + + static data_type ReadData(internal_key_type key, const uint8_t *data, + unsigned length) { + EnumConstantInfo info; + readCommonEntityInfo(data, info); + return info; + } + }; + /// Used to deserialize the on-disk tag table. class TagTableInfo { public: @@ -559,6 +604,12 @@ class APINotesReader::Implementation { /// The global function table. std::unique_ptr GlobalFunctionTable; + using SerializedEnumConstantTable = + llvm::OnDiskIterableChainedHashTable; + + /// The enumerator table. + std::unique_ptr EnumConstantTable; + using SerializedTagTable = llvm::OnDiskIterableChainedHashTable; @@ -595,6 +646,8 @@ class APINotesReader::Implementation { SmallVectorImpl &scratch); bool readGlobalFunctionBlock(llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch); + bool readEnumConstantBlock(llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch); bool readTagBlock(llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch); bool readTypedefBlock(llvm::BitstreamCursor &cursor, @@ -1069,6 +1122,60 @@ bool APINotesReader::Implementation::readGlobalFunctionBlock( return false; } +bool APINotesReader::Implementation::readEnumConstantBlock( + llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch) { + if (cursor.EnterSubBlock(ENUM_CONSTANT_BLOCK_ID)) + return true; + + auto next = cursor.advance(); + while (next.Kind != llvm::BitstreamEntry::EndBlock) { + if (next.Kind == llvm::BitstreamEntry::Error) + return true; + + if (next.Kind == llvm::BitstreamEntry::SubBlock) { + // Unknown sub-block, possibly for use by a future version of the + // API notes format. + if (cursor.SkipBlock()) + return true; + + next = cursor.advance(); + continue; + } + + scratch.clear(); + StringRef blobData; + unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + switch (kind) { + case enum_constant_block::ENUM_CONSTANT_DATA: { + // Already saw enumerator table. + if (EnumConstantTable) + return true; + + uint32_t tableOffset; + enum_constant_block::EnumConstantDataLayout::readRecord(scratch, + tableOffset); + auto base = reinterpret_cast(blobData.data()); + + EnumConstantTable.reset( + SerializedEnumConstantTable::Create(base + tableOffset, + base + sizeof(uint32_t), + base)); + break; + } + + default: + // Unknown record, possibly for use by a future version of the + // module format. + break; + } + + next = cursor.advance(); + } + + return false; +} + bool APINotesReader::Implementation::readTagBlock( llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch) { @@ -1273,6 +1380,14 @@ APINotesReader::APINotesReader(std::unique_ptr inputBuffer, } break; + case ENUM_CONSTANT_BLOCK_ID: + if (!hasValidControlBlock || + Impl.readEnumConstantBlock(cursor, scratch)) { + failed = true; + return; + } + break; + case TAG_BLOCK_ID: if (!hasValidControlBlock || Impl.readTagBlock(cursor, scratch)) { failed = true; @@ -1428,6 +1543,21 @@ Optional APINotesReader::lookupGlobalFunction( return *known; } +Optional APINotesReader::lookupEnumConstant(StringRef name) { + if (!Impl.EnumConstantTable) + return None; + + Optional nameID = Impl.getIdentifier(name); + if (!nameID) + return None; + + auto known = Impl.EnumConstantTable->find(*nameID); + if (known == Impl.EnumConstantTable->end()) + return None; + + return *known; +} + Optional APINotesReader::lookupTag(StringRef name) { if (!Impl.TagTable) return None; @@ -1485,6 +1615,10 @@ void APINotesReader::Visitor::visitGlobalFunction( StringRef name, const GlobalFunctionInfo &info) { } +void APINotesReader::Visitor::visitEnumConstant( + StringRef name, + const EnumConstantInfo &info) { } + void APINotesReader::Visitor::visitTag( StringRef name, const TagInfo &info) { } @@ -1580,6 +1714,15 @@ void APINotesReader::visit(Visitor &visitor) { } } + // Visit global variables. + if (Impl.EnumConstantTable) { + for (auto key : Impl.EnumConstantTable->keys()) { + auto name = identifiers[key]; + auto info = *Impl.EnumConstantTable->find(key); + visitor.visitEnumConstant(name, info); + } + } + // Visit tags. if (Impl.TagTable) { for (auto key : Impl.TagTable->keys()) { diff --git a/lib/APINotes/APINotesWriter.cpp b/lib/APINotes/APINotesWriter.cpp index bc17773ed60..80c1f3487b3 100644 --- a/lib/APINotes/APINotesWriter.cpp +++ b/lib/APINotes/APINotesWriter.cpp @@ -77,6 +77,11 @@ class APINotesWriter::Implementation { /// Indexed by the identifier ID. llvm::DenseMap GlobalFunctions; + /// Information about enumerators. + /// + /// Indexed by the identifier ID. + llvm::DenseMap EnumConstants; + /// Information about tags. /// /// Indexed by the identifier ID. @@ -133,6 +138,7 @@ class APINotesWriter::Implementation { void writeObjCSelectorBlock(llvm::BitstreamWriter &writer); void writeGlobalVariableBlock(llvm::BitstreamWriter &writer); void writeGlobalFunctionBlock(llvm::BitstreamWriter &writer); + void writeEnumConstantBlock(llvm::BitstreamWriter &writer); void writeTagBlock(llvm::BitstreamWriter &writer); void writeTypedefBlock(llvm::BitstreamWriter &writer); }; @@ -754,6 +760,68 @@ void APINotesWriter::Implementation::writeGlobalFunctionBlock( layout.emit(ScratchRecord, tableOffset, hashTableBlob); } +namespace { + /// Used to serialize the on-disk global enum constant. + class EnumConstantTableInfo { + public: + using key_type = unsigned; // name ID + using key_type_ref = key_type; + using data_type = EnumConstantInfo; + using data_type_ref = const data_type &; + using hash_value_type = size_t; + using offset_type = unsigned; + + hash_value_type ComputeHash(key_type_ref key) { + return static_cast(llvm::hash_value(key)); + } + + std::pair EmitKeyDataLength(raw_ostream &out, + key_type_ref key, + data_type_ref data) { + uint32_t keyLength = sizeof(uint32_t); + uint32_t dataLength = getCommonEntityInfoSize(data); + endian::Writer writer(out); + writer.write(keyLength); + writer.write(dataLength); + return { keyLength, dataLength }; + } + + void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { + endian::Writer writer(out); + writer.write(key); + } + + void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, + unsigned len) { + emitCommonEntityInfo(out, data); + } + }; +} // end anonymous namespace + +void APINotesWriter::Implementation::writeEnumConstantBlock( + llvm::BitstreamWriter &writer) { + BCBlockRAII restoreBlock(writer, ENUM_CONSTANT_BLOCK_ID, 3); + + if (EnumConstants.empty()) + return; + + llvm::SmallString<4096> hashTableBlob; + uint32_t tableOffset; + { + llvm::OnDiskChainedHashTableGenerator generator; + for (auto &entry : EnumConstants) + generator.insert(entry.first, entry.second); + + llvm::raw_svector_ostream blobStream(hashTableBlob); + // Make sure that no bucket is at offset 0 + endian::Writer(blobStream).write(0); + tableOffset = generator.Emit(blobStream); + } + + enum_constant_block::EnumConstantDataLayout layout(writer); + layout.emit(ScratchRecord, tableOffset, hashTableBlob); +} + namespace { /// Used to serialize the on-disk tag table. class TagTableInfo { @@ -898,6 +966,7 @@ void APINotesWriter::Implementation::writeToStream(llvm::raw_ostream &os) { writeObjCSelectorBlock(writer); writeGlobalVariableBlock(writer); writeGlobalFunctionBlock(writer); + writeEnumConstantBlock(writer); writeTagBlock(writer); writeTypedefBlock(writer); } @@ -1004,6 +1073,13 @@ void APINotesWriter::addGlobalFunction(llvm::StringRef name, Impl.GlobalFunctions[nameID] = info; } +void APINotesWriter::addEnumConstant(llvm::StringRef name, + const EnumConstantInfo &info) { + IdentifierID enumConstantID = Impl.getIdentifier(name); + assert(!Impl.EnumConstants.count(enumConstantID)); + Impl.EnumConstants[enumConstantID] = info; +} + void APINotesWriter::addTag(llvm::StringRef name, const TagInfo &info) { IdentifierID tagID = Impl.getIdentifier(name); assert(!Impl.Tags.count(tagID)); diff --git a/lib/APINotes/APINotesYAMLCompiler.cpp b/lib/APINotes/APINotesYAMLCompiler.cpp index 39f6c9d6410..9ad6dd6a085 100644 --- a/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/lib/APINotes/APINotesYAMLCompiler.cpp @@ -216,6 +216,13 @@ namespace { }; typedef std::vector GlobalVariablesSeq; + struct EnumConstant { + StringRef Name; + AvailabilityItem Availability; + StringRef SwiftName; + }; + typedef std::vector EnumConstantsSeq; + struct Tag { StringRef Name; AvailabilityItem Availability; @@ -239,6 +246,7 @@ namespace { ClassesSeq Protocols; FunctionsSeq Functions; GlobalVariablesSeq Globals; + EnumConstantsSeq EnumConstants; TagsSeq Tags; TypedefsSeq Typedefs; @@ -254,6 +262,7 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(Property) LLVM_YAML_IS_SEQUENCE_VECTOR(Class) LLVM_YAML_IS_SEQUENCE_VECTOR(Function) LLVM_YAML_IS_SEQUENCE_VECTOR(GlobalVariable) +LLVM_YAML_IS_SEQUENCE_VECTOR(EnumConstant) LLVM_YAML_IS_SEQUENCE_VECTOR(Tag) LLVM_YAML_IS_SEQUENCE_VECTOR(Typedef) @@ -369,6 +378,16 @@ namespace llvm { } }; + template <> + struct MappingTraits { + static void mapping(IO &io, EnumConstant& v) { + io.mapRequired("Name", v.Name); + io.mapOptional("Availability", v.Availability.Mode); + io.mapOptional("AvailabilityMsg", v.Availability.Msg); + io.mapOptional("SwiftName", v.SwiftName); + } + }; + template <> struct MappingTraits { static void mapping(IO &io, Tag& t) { @@ -401,6 +420,7 @@ namespace llvm { io.mapOptional("Protocols", m.Protocols); io.mapOptional("Functions", m.Functions); io.mapOptional("Globals", m.Globals); + io.mapOptional("Enumerators", m.EnumConstants); io.mapOptional("Tags", m.Tags); io.mapOptional("Typedefs", m.Typedefs); } @@ -701,6 +721,24 @@ namespace { Writer->addGlobalFunction(function.Name, info); } + // Write all enumerators. + llvm::StringSet<> knownEnumConstants; + for (const auto &enumConstant : TheModule.EnumConstants) { + // Check for duplicate enumerators + if (!knownEnumConstants.insert(enumConstant.Name).second) { + emitError("multiple definitions of enumerator '" + + enumConstant.Name + "'"); + continue; + } + + EnumConstantInfo info; + if (!isAvailable(enumConstant.Availability)) + continue; + convertAvailability(enumConstant.Availability, info, enumConstant.Name); + info.SwiftName = enumConstant.SwiftName; + Writer->addEnumConstant(enumConstant.Name, info); + } + // Write all tags. llvm::StringSet<> knownTags; for (const auto &t : TheModule.Tags) { @@ -947,6 +985,15 @@ namespace { TheModule.Globals.push_back(global); } + virtual void visitEnumConstant(StringRef name, + const EnumConstantInfo &info) { + EnumConstant enumConstant; + enumConstant.Name = name; + handleCommon(enumConstant, info); + + TheModule.EnumConstants.push_back(enumConstant); + } + virtual void visitTag(StringRef name, const TagInfo &info) { Tag tag; tag.Name = name; @@ -1029,6 +1076,12 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, return lhs.Name < rhs.Name; }); + // Sort enum constants. + std::sort(module.EnumConstants.begin(), module.EnumConstants.end(), + [](const EnumConstant &lhs, const EnumConstant &rhs) -> bool { + return lhs.Name < rhs.Name; + }); + // Sort tags. std::sort(module.Tags.begin(), module.Tags.end(), [](const Tag &lhs, const Tag &rhs) -> bool { diff --git a/lib/Sema/SemaAPINotes.cpp b/lib/Sema/SemaAPINotes.cpp index b7827643d63..34c61b2a7fa 100644 --- a/lib/Sema/SemaAPINotes.cpp +++ b/lib/Sema/SemaAPINotes.cpp @@ -222,6 +222,15 @@ static void ProcessAPINotes(Sema &S, FunctionDecl *D, static_cast(Info)); } +/// Process API notes for an enumerator. +static void ProcessAPINotes(Sema &S, EnumConstantDecl *D, + const api_notes::EnumConstantInfo &Info) { + + // Handle common information. + ProcessAPINotes(S, D, + static_cast(Info)); +} + /// Process API notes for an Objective-C method. static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D, const api_notes::ObjCMethodInfo &Info) { @@ -358,6 +367,20 @@ void Sema::ProcessAPINotes(Decl *D) { return; } + // Enumerators. + if (D->getDeclContext()->getRedeclContext()->isFileContext()) { + if (auto EnumConstant = dyn_cast(D)) { + if (api_notes::APINotesReader *Reader + = APINotes.findAPINotes(D->getLocation())) { + if (auto Info = Reader->lookupEnumConstant(EnumConstant->getName())) { + ::ProcessAPINotes(*this, EnumConstant, *Info); + } + } + + return; + } + } + if (auto ObjCContainer = dyn_cast(D->getDeclContext())) { // Location function that looks up an Objective-C context. auto GetContext = [&](api_notes::APINotesReader *Reader) diff --git a/test/APINotes/Inputs/roundtrip.apinotes b/test/APINotes/Inputs/roundtrip.apinotes index 378c7258d51..c34907ff67d 100644 --- a/test/APINotes/Inputs/roundtrip.apinotes +++ b/test/APINotes/Inputs/roundtrip.apinotes @@ -92,6 +92,11 @@ Globals: Availability: available AvailabilityMsg: '' SwiftName: calibratedWhite +Enumerators: + - Name: NSColorRed + Availability: available + AvailabilityMsg: '' + SwiftName: Red Tags: - Name: NSSomeStruct Availability: available From 3f759cfe9e5b6ef8470a289be837494e1aaba012 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 28 Mar 2016 10:45:01 -0700 Subject: [PATCH 327/742] [API Notes] Add support for enumerators. Addresses the rest of rdar://problem/25365464. --- include/clang/APINotes/APINotesReader.h | 11 ++ include/clang/APINotes/APINotesWriter.h | 6 + include/clang/APINotes/Types.h | 6 + lib/APINotes/APINotesFormat.h | 18 ++- lib/APINotes/APINotesReader.cpp | 143 ++++++++++++++++++++++++ lib/APINotes/APINotesWriter.cpp | 76 +++++++++++++ lib/APINotes/APINotesYAMLCompiler.cpp | 53 +++++++++ lib/Sema/SemaAPINotes.cpp | 23 ++++ test/APINotes/Inputs/roundtrip.apinotes | 5 + 9 files changed, 340 insertions(+), 1 deletion(-) diff --git a/include/clang/APINotes/APINotesReader.h b/include/clang/APINotes/APINotesReader.h index 16cb5ebe109..839e1dc5ee9 100644 --- a/include/clang/APINotes/APINotesReader.h +++ b/include/clang/APINotes/APINotesReader.h @@ -102,6 +102,13 @@ class APINotesReader { /// \returns information about the global function, if known. Optional lookupGlobalFunction(StringRef name); + /// Look for information regarding the given enumerator. + /// + /// \param name The name of the enumerator. + /// + /// \returns information about the enumerator, if known. + Optional lookupEnumConstant(StringRef name); + /// Look for information regarding the given tag /// (struct/union/enum/C++ class). /// @@ -147,6 +154,10 @@ class APINotesReader { virtual void visitGlobalFunction(StringRef name, const GlobalFunctionInfo &info); + /// Visit an enumerator. + virtual void visitEnumConstant(StringRef name, + const EnumConstantInfo &info); + /// Visit a tag. virtual void visitTag(StringRef name, const TagInfo &info); diff --git a/include/clang/APINotes/APINotesWriter.h b/include/clang/APINotes/APINotesWriter.h index 38935a6a271..66dc8ffebba 100644 --- a/include/clang/APINotes/APINotesWriter.h +++ b/include/clang/APINotes/APINotesWriter.h @@ -90,6 +90,12 @@ class APINotesWriter { /// \param info Information about this global function. void addGlobalFunction(StringRef name, const GlobalFunctionInfo &info); + /// Add information about an enumerator. + /// + /// \param name The name of this enumerator. + /// \param info Information about this enumerator. + void addEnumConstant(StringRef name, const EnumConstantInfo &info); + /// Add information about a tag (struct/union/enum/C++ class). /// /// \param name The name of this tag. diff --git a/include/clang/APINotes/Types.h b/include/clang/APINotes/Types.h index 5053190f408..2fc4ee8fe73 100644 --- a/include/clang/APINotes/Types.h +++ b/include/clang/APINotes/Types.h @@ -456,6 +456,12 @@ class GlobalFunctionInfo : public FunctionInfo { GlobalFunctionInfo() : FunctionInfo() { } }; +/// Describes API notes data for an enumerator. +class EnumConstantInfo : public CommonEntityInfo { +public: + EnumConstantInfo() : CommonEntityInfo() { } +}; + /// Describes API notes data for a tag. class TagInfo : public CommonTypeInfo { public: diff --git a/lib/APINotes/APINotesFormat.h b/lib/APINotes/APINotesFormat.h index 4cdad891841..d344a30a661 100644 --- a/lib/APINotes/APINotesFormat.h +++ b/lib/APINotes/APINotesFormat.h @@ -35,7 +35,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 9; +const uint16_t VERSION_MINOR = 10; // enum constants using IdentifierID = Fixnum<31>; using IdentifierIDField = BCVBR<16>; @@ -93,6 +93,10 @@ enum BlockID { /// The typedef data block, which maps typedef names to information about /// the typedefs. TYPEDEF_BLOCK_ID, + + /// The enum constant data block, which maps enumerator names to + /// information about the enumerators. + ENUM_CONSTANT_BLOCK_ID, }; namespace control_block { @@ -225,6 +229,18 @@ namespace typedef_block { >; }; +namespace enum_constant_block { + enum { + ENUM_CONSTANT_DATA = 1 + }; + + using EnumConstantDataLayout = BCRecordLayout< + ENUM_CONSTANT_DATA, // record ID + BCVBR<16>, // table offset within the blob (see below) + BCBlob // map from name to enumerator information + >; +} + /// A stored Objective-C selector. struct StoredObjCSelector { unsigned NumPieces; diff --git a/lib/APINotes/APINotesReader.cpp b/lib/APINotes/APINotesReader.cpp index b4d8f5750e4..62acecf3cc8 100644 --- a/lib/APINotes/APINotesReader.cpp +++ b/lib/APINotes/APINotesReader.cpp @@ -415,6 +415,51 @@ namespace { } }; + /// Used to deserialize the on-disk enumerator table. + class EnumConstantTableInfo { + public: + using internal_key_type = unsigned; // name ID + using external_key_type = internal_key_type; + using data_type = EnumConstantInfo; + using hash_value_type = size_t; + using offset_type = unsigned; + + internal_key_type GetInternalKey(external_key_type key) { + return key; + } + + external_key_type GetExternalKey(internal_key_type key) { + return key; + } + + hash_value_type ComputeHash(internal_key_type key) { + return static_cast(llvm::hash_value(key)); + } + + static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { + return lhs == rhs; + } + + static std::pair + ReadKeyDataLength(const uint8_t *&data) { + unsigned keyLength = endian::readNext(data); + unsigned dataLength = endian::readNext(data); + return { keyLength, dataLength }; + } + + static internal_key_type ReadKey(const uint8_t *data, unsigned length) { + auto nameID = endian::readNext(data); + return nameID; + } + + static data_type ReadData(internal_key_type key, const uint8_t *data, + unsigned length) { + EnumConstantInfo info; + readCommonEntityInfo(data, info); + return info; + } + }; + /// Used to deserialize the on-disk tag table. class TagTableInfo { public: @@ -559,6 +604,12 @@ class APINotesReader::Implementation { /// The global function table. std::unique_ptr GlobalFunctionTable; + using SerializedEnumConstantTable = + llvm::OnDiskIterableChainedHashTable; + + /// The enumerator table. + std::unique_ptr EnumConstantTable; + using SerializedTagTable = llvm::OnDiskIterableChainedHashTable; @@ -595,6 +646,8 @@ class APINotesReader::Implementation { SmallVectorImpl &scratch); bool readGlobalFunctionBlock(llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch); + bool readEnumConstantBlock(llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch); bool readTagBlock(llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch); bool readTypedefBlock(llvm::BitstreamCursor &cursor, @@ -1069,6 +1122,60 @@ bool APINotesReader::Implementation::readGlobalFunctionBlock( return false; } +bool APINotesReader::Implementation::readEnumConstantBlock( + llvm::BitstreamCursor &cursor, + SmallVectorImpl &scratch) { + if (cursor.EnterSubBlock(ENUM_CONSTANT_BLOCK_ID)) + return true; + + auto next = cursor.advance(); + while (next.Kind != llvm::BitstreamEntry::EndBlock) { + if (next.Kind == llvm::BitstreamEntry::Error) + return true; + + if (next.Kind == llvm::BitstreamEntry::SubBlock) { + // Unknown sub-block, possibly for use by a future version of the + // API notes format. + if (cursor.SkipBlock()) + return true; + + next = cursor.advance(); + continue; + } + + scratch.clear(); + StringRef blobData; + unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); + switch (kind) { + case enum_constant_block::ENUM_CONSTANT_DATA: { + // Already saw enumerator table. + if (EnumConstantTable) + return true; + + uint32_t tableOffset; + enum_constant_block::EnumConstantDataLayout::readRecord(scratch, + tableOffset); + auto base = reinterpret_cast(blobData.data()); + + EnumConstantTable.reset( + SerializedEnumConstantTable::Create(base + tableOffset, + base + sizeof(uint32_t), + base)); + break; + } + + default: + // Unknown record, possibly for use by a future version of the + // module format. + break; + } + + next = cursor.advance(); + } + + return false; +} + bool APINotesReader::Implementation::readTagBlock( llvm::BitstreamCursor &cursor, SmallVectorImpl &scratch) { @@ -1273,6 +1380,14 @@ APINotesReader::APINotesReader(std::unique_ptr inputBuffer, } break; + case ENUM_CONSTANT_BLOCK_ID: + if (!hasValidControlBlock || + Impl.readEnumConstantBlock(cursor, scratch)) { + failed = true; + return; + } + break; + case TAG_BLOCK_ID: if (!hasValidControlBlock || Impl.readTagBlock(cursor, scratch)) { failed = true; @@ -1428,6 +1543,21 @@ Optional APINotesReader::lookupGlobalFunction( return *known; } +Optional APINotesReader::lookupEnumConstant(StringRef name) { + if (!Impl.EnumConstantTable) + return None; + + Optional nameID = Impl.getIdentifier(name); + if (!nameID) + return None; + + auto known = Impl.EnumConstantTable->find(*nameID); + if (known == Impl.EnumConstantTable->end()) + return None; + + return *known; +} + Optional APINotesReader::lookupTag(StringRef name) { if (!Impl.TagTable) return None; @@ -1485,6 +1615,10 @@ void APINotesReader::Visitor::visitGlobalFunction( StringRef name, const GlobalFunctionInfo &info) { } +void APINotesReader::Visitor::visitEnumConstant( + StringRef name, + const EnumConstantInfo &info) { } + void APINotesReader::Visitor::visitTag( StringRef name, const TagInfo &info) { } @@ -1580,6 +1714,15 @@ void APINotesReader::visit(Visitor &visitor) { } } + // Visit global variables. + if (Impl.EnumConstantTable) { + for (auto key : Impl.EnumConstantTable->keys()) { + auto name = identifiers[key]; + auto info = *Impl.EnumConstantTable->find(key); + visitor.visitEnumConstant(name, info); + } + } + // Visit tags. if (Impl.TagTable) { for (auto key : Impl.TagTable->keys()) { diff --git a/lib/APINotes/APINotesWriter.cpp b/lib/APINotes/APINotesWriter.cpp index bc17773ed60..80c1f3487b3 100644 --- a/lib/APINotes/APINotesWriter.cpp +++ b/lib/APINotes/APINotesWriter.cpp @@ -77,6 +77,11 @@ class APINotesWriter::Implementation { /// Indexed by the identifier ID. llvm::DenseMap GlobalFunctions; + /// Information about enumerators. + /// + /// Indexed by the identifier ID. + llvm::DenseMap EnumConstants; + /// Information about tags. /// /// Indexed by the identifier ID. @@ -133,6 +138,7 @@ class APINotesWriter::Implementation { void writeObjCSelectorBlock(llvm::BitstreamWriter &writer); void writeGlobalVariableBlock(llvm::BitstreamWriter &writer); void writeGlobalFunctionBlock(llvm::BitstreamWriter &writer); + void writeEnumConstantBlock(llvm::BitstreamWriter &writer); void writeTagBlock(llvm::BitstreamWriter &writer); void writeTypedefBlock(llvm::BitstreamWriter &writer); }; @@ -754,6 +760,68 @@ void APINotesWriter::Implementation::writeGlobalFunctionBlock( layout.emit(ScratchRecord, tableOffset, hashTableBlob); } +namespace { + /// Used to serialize the on-disk global enum constant. + class EnumConstantTableInfo { + public: + using key_type = unsigned; // name ID + using key_type_ref = key_type; + using data_type = EnumConstantInfo; + using data_type_ref = const data_type &; + using hash_value_type = size_t; + using offset_type = unsigned; + + hash_value_type ComputeHash(key_type_ref key) { + return static_cast(llvm::hash_value(key)); + } + + std::pair EmitKeyDataLength(raw_ostream &out, + key_type_ref key, + data_type_ref data) { + uint32_t keyLength = sizeof(uint32_t); + uint32_t dataLength = getCommonEntityInfoSize(data); + endian::Writer writer(out); + writer.write(keyLength); + writer.write(dataLength); + return { keyLength, dataLength }; + } + + void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { + endian::Writer writer(out); + writer.write(key); + } + + void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, + unsigned len) { + emitCommonEntityInfo(out, data); + } + }; +} // end anonymous namespace + +void APINotesWriter::Implementation::writeEnumConstantBlock( + llvm::BitstreamWriter &writer) { + BCBlockRAII restoreBlock(writer, ENUM_CONSTANT_BLOCK_ID, 3); + + if (EnumConstants.empty()) + return; + + llvm::SmallString<4096> hashTableBlob; + uint32_t tableOffset; + { + llvm::OnDiskChainedHashTableGenerator generator; + for (auto &entry : EnumConstants) + generator.insert(entry.first, entry.second); + + llvm::raw_svector_ostream blobStream(hashTableBlob); + // Make sure that no bucket is at offset 0 + endian::Writer(blobStream).write(0); + tableOffset = generator.Emit(blobStream); + } + + enum_constant_block::EnumConstantDataLayout layout(writer); + layout.emit(ScratchRecord, tableOffset, hashTableBlob); +} + namespace { /// Used to serialize the on-disk tag table. class TagTableInfo { @@ -898,6 +966,7 @@ void APINotesWriter::Implementation::writeToStream(llvm::raw_ostream &os) { writeObjCSelectorBlock(writer); writeGlobalVariableBlock(writer); writeGlobalFunctionBlock(writer); + writeEnumConstantBlock(writer); writeTagBlock(writer); writeTypedefBlock(writer); } @@ -1004,6 +1073,13 @@ void APINotesWriter::addGlobalFunction(llvm::StringRef name, Impl.GlobalFunctions[nameID] = info; } +void APINotesWriter::addEnumConstant(llvm::StringRef name, + const EnumConstantInfo &info) { + IdentifierID enumConstantID = Impl.getIdentifier(name); + assert(!Impl.EnumConstants.count(enumConstantID)); + Impl.EnumConstants[enumConstantID] = info; +} + void APINotesWriter::addTag(llvm::StringRef name, const TagInfo &info) { IdentifierID tagID = Impl.getIdentifier(name); assert(!Impl.Tags.count(tagID)); diff --git a/lib/APINotes/APINotesYAMLCompiler.cpp b/lib/APINotes/APINotesYAMLCompiler.cpp index 39f6c9d6410..9ad6dd6a085 100644 --- a/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/lib/APINotes/APINotesYAMLCompiler.cpp @@ -216,6 +216,13 @@ namespace { }; typedef std::vector GlobalVariablesSeq; + struct EnumConstant { + StringRef Name; + AvailabilityItem Availability; + StringRef SwiftName; + }; + typedef std::vector EnumConstantsSeq; + struct Tag { StringRef Name; AvailabilityItem Availability; @@ -239,6 +246,7 @@ namespace { ClassesSeq Protocols; FunctionsSeq Functions; GlobalVariablesSeq Globals; + EnumConstantsSeq EnumConstants; TagsSeq Tags; TypedefsSeq Typedefs; @@ -254,6 +262,7 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(Property) LLVM_YAML_IS_SEQUENCE_VECTOR(Class) LLVM_YAML_IS_SEQUENCE_VECTOR(Function) LLVM_YAML_IS_SEQUENCE_VECTOR(GlobalVariable) +LLVM_YAML_IS_SEQUENCE_VECTOR(EnumConstant) LLVM_YAML_IS_SEQUENCE_VECTOR(Tag) LLVM_YAML_IS_SEQUENCE_VECTOR(Typedef) @@ -369,6 +378,16 @@ namespace llvm { } }; + template <> + struct MappingTraits { + static void mapping(IO &io, EnumConstant& v) { + io.mapRequired("Name", v.Name); + io.mapOptional("Availability", v.Availability.Mode); + io.mapOptional("AvailabilityMsg", v.Availability.Msg); + io.mapOptional("SwiftName", v.SwiftName); + } + }; + template <> struct MappingTraits { static void mapping(IO &io, Tag& t) { @@ -401,6 +420,7 @@ namespace llvm { io.mapOptional("Protocols", m.Protocols); io.mapOptional("Functions", m.Functions); io.mapOptional("Globals", m.Globals); + io.mapOptional("Enumerators", m.EnumConstants); io.mapOptional("Tags", m.Tags); io.mapOptional("Typedefs", m.Typedefs); } @@ -701,6 +721,24 @@ namespace { Writer->addGlobalFunction(function.Name, info); } + // Write all enumerators. + llvm::StringSet<> knownEnumConstants; + for (const auto &enumConstant : TheModule.EnumConstants) { + // Check for duplicate enumerators + if (!knownEnumConstants.insert(enumConstant.Name).second) { + emitError("multiple definitions of enumerator '" + + enumConstant.Name + "'"); + continue; + } + + EnumConstantInfo info; + if (!isAvailable(enumConstant.Availability)) + continue; + convertAvailability(enumConstant.Availability, info, enumConstant.Name); + info.SwiftName = enumConstant.SwiftName; + Writer->addEnumConstant(enumConstant.Name, info); + } + // Write all tags. llvm::StringSet<> knownTags; for (const auto &t : TheModule.Tags) { @@ -947,6 +985,15 @@ namespace { TheModule.Globals.push_back(global); } + virtual void visitEnumConstant(StringRef name, + const EnumConstantInfo &info) { + EnumConstant enumConstant; + enumConstant.Name = name; + handleCommon(enumConstant, info); + + TheModule.EnumConstants.push_back(enumConstant); + } + virtual void visitTag(StringRef name, const TagInfo &info) { Tag tag; tag.Name = name; @@ -1029,6 +1076,12 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, return lhs.Name < rhs.Name; }); + // Sort enum constants. + std::sort(module.EnumConstants.begin(), module.EnumConstants.end(), + [](const EnumConstant &lhs, const EnumConstant &rhs) -> bool { + return lhs.Name < rhs.Name; + }); + // Sort tags. std::sort(module.Tags.begin(), module.Tags.end(), [](const Tag &lhs, const Tag &rhs) -> bool { diff --git a/lib/Sema/SemaAPINotes.cpp b/lib/Sema/SemaAPINotes.cpp index fd70e94cd32..bef88e2f872 100644 --- a/lib/Sema/SemaAPINotes.cpp +++ b/lib/Sema/SemaAPINotes.cpp @@ -224,6 +224,15 @@ static void ProcessAPINotes(Sema &S, FunctionDecl *D, static_cast(Info)); } +/// Process API notes for an enumerator. +static void ProcessAPINotes(Sema &S, EnumConstantDecl *D, + const api_notes::EnumConstantInfo &Info) { + + // Handle common information. + ProcessAPINotes(S, D, + static_cast(Info)); +} + /// Process API notes for an Objective-C method. static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D, const api_notes::ObjCMethodInfo &Info) { @@ -360,6 +369,20 @@ void Sema::ProcessAPINotes(Decl *D) { return; } + // Enumerators. + if (D->getDeclContext()->getRedeclContext()->isFileContext()) { + if (auto EnumConstant = dyn_cast(D)) { + if (api_notes::APINotesReader *Reader + = APINotes.findAPINotes(D->getLocation())) { + if (auto Info = Reader->lookupEnumConstant(EnumConstant->getName())) { + ::ProcessAPINotes(*this, EnumConstant, *Info); + } + } + + return; + } + } + if (auto ObjCContainer = dyn_cast(D->getDeclContext())) { // Location function that looks up an Objective-C context. auto GetContext = [&](api_notes::APINotesReader *Reader) diff --git a/test/APINotes/Inputs/roundtrip.apinotes b/test/APINotes/Inputs/roundtrip.apinotes index 378c7258d51..c34907ff67d 100644 --- a/test/APINotes/Inputs/roundtrip.apinotes +++ b/test/APINotes/Inputs/roundtrip.apinotes @@ -92,6 +92,11 @@ Globals: Availability: available AvailabilityMsg: '' SwiftName: calibratedWhite +Enumerators: + - Name: NSColorRed + Availability: available + AvailabilityMsg: '' + SwiftName: Red Tags: - Name: NSSomeStruct Availability: available From c52d85ede49adaec457400697b1d4c1b7ea12881 Mon Sep 17 00:00:00 2001 From: Sean Callanan Date: Mon, 28 Mar 2016 21:43:01 +0000 Subject: [PATCH 328/742] Improvements to the ASTImporter to support LLDB top-level Clang expressions. The testcase for this is in LLDB, adeed by r264662. This patch adds support for a variety of new expression types to the AST importer, mostly related to C++. It also adds support for importing lambdas correctly, and adds support for importing the attributes attached to any Decl. Finally, the patch adds a new templated function to ASTNodeImporter that imports arbitrary arrays of importable things into a bump-allocated array attached to getToContext(). This is a pattern we see at many places in ASTNodeImporter; rather than do it slightly differently at each point, this function does it one way. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@264669 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ASTImporter.cpp | 150 +++++++++++++++++++++++++++++++++------- 1 file changed, 124 insertions(+), 26 deletions(-) diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 018fafff9c6..56d59952692 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -223,8 +223,36 @@ namespace clang { Expr *VisitImplicitCastExpr(ImplicitCastExpr *E); Expr *VisitCStyleCastExpr(CStyleCastExpr *E); Expr *VisitCXXConstructExpr(CXXConstructExpr *E); + Expr *VisitCXXMemberCallExpr(CXXMemberCallExpr *E); + Expr *VisitCXXThisExpr(CXXThisExpr *E); + Expr *VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *E); Expr *VisitMemberExpr(MemberExpr *E); Expr *VisitCallExpr(CallExpr *E); + Expr *VisitInitListExpr(InitListExpr *E); + + template bool ImportArray(Iter B, Iter E, llvm::ArrayRef &ToArray) { + size_t NumElements = E - B; + SmallVector ImportedElements(NumElements); + ASTImporter &_Importer = Importer; + + bool Failed = false; + std::transform(B, E, ImportedElements.begin(), + [&_Importer, &Failed](T *Element) -> T* { + T *ToElement = _Importer.Import(Element); + if (Element && !ToElement) + Failed = true; + return ToElement; + }); + + if (Failed) + return false; + + T **CopiedElements = new (Importer.getToContext()) T*[NumElements]; + std::copy(ImportedElements.begin(), ImportedElements.end(), &CopiedElements[0]); + ToArray = llvm::ArrayRef(CopiedElements, NumElements); + + return true; + } }; } using namespace clang; @@ -2654,11 +2682,26 @@ Decl *ASTNodeImporter::VisitRecordDecl(RecordDecl *D) { RecordDecl *D2 = AdoptDecl; SourceLocation StartLoc = Importer.Import(D->getLocStart()); if (!D2) { - if (isa(D)) { - CXXRecordDecl *D2CXX = CXXRecordDecl::Create(Importer.getToContext(), - D->getTagKind(), - DC, StartLoc, Loc, - Name.getAsIdentifierInfo()); + CXXRecordDecl *D2CXX = nullptr; + if (CXXRecordDecl *DCXX = llvm::dyn_cast(D)) { + if (DCXX->isLambda()) { + TypeSourceInfo *TInfo = Importer.Import(DCXX->getLambdaTypeInfo()); + D2CXX = CXXRecordDecl::CreateLambda(Importer.getToContext(), + DC, TInfo, Loc, + DCXX->isDependentLambda(), + DCXX->isGenericLambda(), + DCXX->getLambdaCaptureDefault()); + Decl *CDecl = Importer.Import(DCXX->getLambdaContextDecl()); + if (DCXX->getLambdaContextDecl() && !CDecl) + return nullptr; + D2CXX->setLambdaMangling(DCXX->getLambdaManglingNumber(), + CDecl); + } else { + D2CXX = CXXRecordDecl::Create(Importer.getToContext(), + D->getTagKind(), + DC, StartLoc, Loc, + Name.getAsIdentifierInfo()); + } D2 = D2CXX; D2->setAccess(D->getAccess()); } else { @@ -4619,16 +4662,11 @@ Stmt *ASTNodeImporter::VisitNullStmt(NullStmt *S) { } Stmt *ASTNodeImporter::VisitCompoundStmt(CompoundStmt *S) { - SmallVector ToStmts(S->size()); - auto &_Importer = this->Importer; - std::transform(S->body_begin(), S->body_end(), ToStmts.begin(), - [&_Importer](Stmt *CS) -> Stmt * { - return _Importer.Import(CS); - }); - for (Stmt *ToS : ToStmts) { - if (!ToS) - return nullptr; - } + llvm::ArrayRef ToStmts; + + if (!ImportArray(S->body_begin(), S->body_end(), ToStmts)) + return nullptr; + SourceLocation ToLBraceLoc = Importer.Import(S->getLBracLoc()); SourceLocation ToRBraceLoc = Importer.Import(S->getRBracLoc()); return new (Importer.getToContext()) CompoundStmt(Importer.getToContext(), @@ -5252,17 +5290,10 @@ Expr *ASTNodeImporter::VisitCXXConstructExpr(CXXConstructExpr *E) { if (!ToCCD && E->getConstructor()) return nullptr; - size_t NumArgs = E->getNumArgs(); - SmallVector ToArgs(NumArgs); - ASTImporter &_Importer = Importer; - std::transform(E->arg_begin(), E->arg_end(), ToArgs.begin(), - [&_Importer](Expr *AE) -> Expr * { - return _Importer.Import(AE); - }); - for (Expr *ToA : ToArgs) { - if (!ToA) - return nullptr; - } + ArrayRef ToArgs; + + if (!ImportArray(E->arg_begin(), E->arg_end(), ToArgs)) + return nullptr; return CXXConstructExpr::Create(Importer.getToContext(), T, Importer.Import(E->getLocation()), @@ -5275,6 +5306,44 @@ Expr *ASTNodeImporter::VisitCXXConstructExpr(CXXConstructExpr *E) { Importer.Import(E->getParenOrBraceRange())); } +Expr *ASTNodeImporter::VisitCXXMemberCallExpr(CXXMemberCallExpr *E) { + QualType T = Importer.Import(E->getType()); + if (T.isNull()) + return nullptr; + + Expr *ToFn = Importer.Import(E->getCallee()); + if (!ToFn) + return nullptr; + + ArrayRef ToArgs; + + if (!ImportArray(E->arg_begin(), E->arg_end(), ToArgs)) + return nullptr; + + return new (Importer.getToContext()) CXXMemberCallExpr(Importer.getToContext(), ToFn, + ToArgs, T, E->getValueKind(), + Importer.Import(E->getRParenLoc())); +} + +Expr *ASTNodeImporter::VisitCXXThisExpr(CXXThisExpr *E) { + QualType T = Importer.Import(E->getType()); + if (T.isNull()) + return nullptr; + + return new (Importer.getToContext()) + CXXThisExpr(Importer.Import(E->getLocation()), T, E->isImplicit()); +} + +Expr *ASTNodeImporter::VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *E) { + QualType T = Importer.Import(E->getType()); + if (T.isNull()) + return nullptr; + + return new (Importer.getToContext()) + CXXBoolLiteralExpr(E->getValue(), T, Importer.Import(E->getLocation())); +} + + Expr *ASTNodeImporter::VisitMemberExpr(MemberExpr *E) { QualType T = Importer.Import(E->getType()); if (T.isNull()) @@ -5343,6 +5412,28 @@ Expr *ASTNodeImporter::VisitCallExpr(CallExpr *E) { Importer.Import(E->getRParenLoc())); } +Expr *ASTNodeImporter::VisitInitListExpr(InitListExpr *E) { + QualType T = Importer.Import(E->getType()); + if (T.isNull()) + return nullptr; + + ArrayRef ToInits; + + if (!ImportArray(E->inits().begin(), E->inits().end(), ToInits)) + return nullptr; + + InitListExpr *ToE = new (Importer.getToContext()) + InitListExpr(Importer.getToContext(), + Importer.Import(E->getLBraceLoc()), + ToInits, + Importer.Import(E->getRBraceLoc())); + + if (ToE) + ToE->setType(T); + + return ToE; +} + ASTImporter::ASTImporter(ASTContext &ToContext, FileManager &ToFileManager, ASTContext &FromContext, FileManager &FromFileManager, bool MinimalImport) @@ -5911,6 +6002,13 @@ void ASTImporter::CompleteDecl (Decl *D) { } Decl *ASTImporter::Imported(Decl *From, Decl *To) { + if (From->hasAttrs()) { + for (Attr *FromAttr : From->getAttrs()) + To->addAttr(FromAttr->clone(To->getASTContext())); + } + if (From->isUsed()) { + To->setIsUsed(); + } ImportedDecls[From] = To; return To; } From 7e769d0f210219a94da74b20045c7f2a710a4c8c Mon Sep 17 00:00:00 2001 From: Sean Callanan Date: Mon, 28 Mar 2016 21:43:01 +0000 Subject: [PATCH 329/742] Improvements to the ASTImporter to support LLDB top-level Clang expressions. The testcase for this is in LLDB, adeed by r264662. This patch adds support for a variety of new expression types to the AST importer, mostly related to C++. It also adds support for importing lambdas correctly, and adds support for importing the attributes attached to any Decl. Finally, the patch adds a new templated function to ASTNodeImporter that imports arbitrary arrays of importable things into a bump-allocated array attached to getToContext(). This is a pattern we see at many places in ASTNodeImporter; rather than do it slightly differently at each point, this function does it one way. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@264669 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ASTImporter.cpp | 150 +++++++++++++++++++++++++++++++++------- 1 file changed, 124 insertions(+), 26 deletions(-) diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 018fafff9c6..56d59952692 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -223,8 +223,36 @@ namespace clang { Expr *VisitImplicitCastExpr(ImplicitCastExpr *E); Expr *VisitCStyleCastExpr(CStyleCastExpr *E); Expr *VisitCXXConstructExpr(CXXConstructExpr *E); + Expr *VisitCXXMemberCallExpr(CXXMemberCallExpr *E); + Expr *VisitCXXThisExpr(CXXThisExpr *E); + Expr *VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *E); Expr *VisitMemberExpr(MemberExpr *E); Expr *VisitCallExpr(CallExpr *E); + Expr *VisitInitListExpr(InitListExpr *E); + + template bool ImportArray(Iter B, Iter E, llvm::ArrayRef &ToArray) { + size_t NumElements = E - B; + SmallVector ImportedElements(NumElements); + ASTImporter &_Importer = Importer; + + bool Failed = false; + std::transform(B, E, ImportedElements.begin(), + [&_Importer, &Failed](T *Element) -> T* { + T *ToElement = _Importer.Import(Element); + if (Element && !ToElement) + Failed = true; + return ToElement; + }); + + if (Failed) + return false; + + T **CopiedElements = new (Importer.getToContext()) T*[NumElements]; + std::copy(ImportedElements.begin(), ImportedElements.end(), &CopiedElements[0]); + ToArray = llvm::ArrayRef(CopiedElements, NumElements); + + return true; + } }; } using namespace clang; @@ -2654,11 +2682,26 @@ Decl *ASTNodeImporter::VisitRecordDecl(RecordDecl *D) { RecordDecl *D2 = AdoptDecl; SourceLocation StartLoc = Importer.Import(D->getLocStart()); if (!D2) { - if (isa(D)) { - CXXRecordDecl *D2CXX = CXXRecordDecl::Create(Importer.getToContext(), - D->getTagKind(), - DC, StartLoc, Loc, - Name.getAsIdentifierInfo()); + CXXRecordDecl *D2CXX = nullptr; + if (CXXRecordDecl *DCXX = llvm::dyn_cast(D)) { + if (DCXX->isLambda()) { + TypeSourceInfo *TInfo = Importer.Import(DCXX->getLambdaTypeInfo()); + D2CXX = CXXRecordDecl::CreateLambda(Importer.getToContext(), + DC, TInfo, Loc, + DCXX->isDependentLambda(), + DCXX->isGenericLambda(), + DCXX->getLambdaCaptureDefault()); + Decl *CDecl = Importer.Import(DCXX->getLambdaContextDecl()); + if (DCXX->getLambdaContextDecl() && !CDecl) + return nullptr; + D2CXX->setLambdaMangling(DCXX->getLambdaManglingNumber(), + CDecl); + } else { + D2CXX = CXXRecordDecl::Create(Importer.getToContext(), + D->getTagKind(), + DC, StartLoc, Loc, + Name.getAsIdentifierInfo()); + } D2 = D2CXX; D2->setAccess(D->getAccess()); } else { @@ -4619,16 +4662,11 @@ Stmt *ASTNodeImporter::VisitNullStmt(NullStmt *S) { } Stmt *ASTNodeImporter::VisitCompoundStmt(CompoundStmt *S) { - SmallVector ToStmts(S->size()); - auto &_Importer = this->Importer; - std::transform(S->body_begin(), S->body_end(), ToStmts.begin(), - [&_Importer](Stmt *CS) -> Stmt * { - return _Importer.Import(CS); - }); - for (Stmt *ToS : ToStmts) { - if (!ToS) - return nullptr; - } + llvm::ArrayRef ToStmts; + + if (!ImportArray(S->body_begin(), S->body_end(), ToStmts)) + return nullptr; + SourceLocation ToLBraceLoc = Importer.Import(S->getLBracLoc()); SourceLocation ToRBraceLoc = Importer.Import(S->getRBracLoc()); return new (Importer.getToContext()) CompoundStmt(Importer.getToContext(), @@ -5252,17 +5290,10 @@ Expr *ASTNodeImporter::VisitCXXConstructExpr(CXXConstructExpr *E) { if (!ToCCD && E->getConstructor()) return nullptr; - size_t NumArgs = E->getNumArgs(); - SmallVector ToArgs(NumArgs); - ASTImporter &_Importer = Importer; - std::transform(E->arg_begin(), E->arg_end(), ToArgs.begin(), - [&_Importer](Expr *AE) -> Expr * { - return _Importer.Import(AE); - }); - for (Expr *ToA : ToArgs) { - if (!ToA) - return nullptr; - } + ArrayRef ToArgs; + + if (!ImportArray(E->arg_begin(), E->arg_end(), ToArgs)) + return nullptr; return CXXConstructExpr::Create(Importer.getToContext(), T, Importer.Import(E->getLocation()), @@ -5275,6 +5306,44 @@ Expr *ASTNodeImporter::VisitCXXConstructExpr(CXXConstructExpr *E) { Importer.Import(E->getParenOrBraceRange())); } +Expr *ASTNodeImporter::VisitCXXMemberCallExpr(CXXMemberCallExpr *E) { + QualType T = Importer.Import(E->getType()); + if (T.isNull()) + return nullptr; + + Expr *ToFn = Importer.Import(E->getCallee()); + if (!ToFn) + return nullptr; + + ArrayRef ToArgs; + + if (!ImportArray(E->arg_begin(), E->arg_end(), ToArgs)) + return nullptr; + + return new (Importer.getToContext()) CXXMemberCallExpr(Importer.getToContext(), ToFn, + ToArgs, T, E->getValueKind(), + Importer.Import(E->getRParenLoc())); +} + +Expr *ASTNodeImporter::VisitCXXThisExpr(CXXThisExpr *E) { + QualType T = Importer.Import(E->getType()); + if (T.isNull()) + return nullptr; + + return new (Importer.getToContext()) + CXXThisExpr(Importer.Import(E->getLocation()), T, E->isImplicit()); +} + +Expr *ASTNodeImporter::VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *E) { + QualType T = Importer.Import(E->getType()); + if (T.isNull()) + return nullptr; + + return new (Importer.getToContext()) + CXXBoolLiteralExpr(E->getValue(), T, Importer.Import(E->getLocation())); +} + + Expr *ASTNodeImporter::VisitMemberExpr(MemberExpr *E) { QualType T = Importer.Import(E->getType()); if (T.isNull()) @@ -5343,6 +5412,28 @@ Expr *ASTNodeImporter::VisitCallExpr(CallExpr *E) { Importer.Import(E->getRParenLoc())); } +Expr *ASTNodeImporter::VisitInitListExpr(InitListExpr *E) { + QualType T = Importer.Import(E->getType()); + if (T.isNull()) + return nullptr; + + ArrayRef ToInits; + + if (!ImportArray(E->inits().begin(), E->inits().end(), ToInits)) + return nullptr; + + InitListExpr *ToE = new (Importer.getToContext()) + InitListExpr(Importer.getToContext(), + Importer.Import(E->getLBraceLoc()), + ToInits, + Importer.Import(E->getRBraceLoc())); + + if (ToE) + ToE->setType(T); + + return ToE; +} + ASTImporter::ASTImporter(ASTContext &ToContext, FileManager &ToFileManager, ASTContext &FromContext, FileManager &FromFileManager, bool MinimalImport) @@ -5911,6 +6002,13 @@ void ASTImporter::CompleteDecl (Decl *D) { } Decl *ASTImporter::Imported(Decl *From, Decl *To) { + if (From->hasAttrs()) { + for (Attr *FromAttr : From->getAttrs()) + To->addAttr(FromAttr->clone(To->getASTContext())); + } + if (From->isUsed()) { + To->setIsUsed(); + } ImportedDecls[From] = To; return To; } From 07952401474b71b299b07d771523ef824509667d Mon Sep 17 00:00:00 2001 From: Justin Bogner Date: Tue, 22 Mar 2016 17:50:05 +0000 Subject: [PATCH 330/742] StaticAnalyzer: Avoid an unintentional copy The range here isn't over references, so using `auto &` here incites a copy. Switching to `auto *` would do, but we might as well list an explicit type for clarity. Found by -Wrange-loop-analysis. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@264071 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 9872ddf47c0247ab5e43568947fed51759e83755) --- lib/StaticAnalyzer/Checkers/PaddingChecker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp b/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp index 05bb7b71b8a..0640d2f49f4 100644 --- a/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp @@ -168,7 +168,7 @@ class PaddingChecker : public Checker> { const ASTRecordLayout &RL) { CharUnits PaddingSum; CharUnits Offset = ASTContext.toCharUnitsFromBits(RL.getFieldOffset(0)); - for (const auto &FD : RD->fields()) { + for (const FieldDecl *FD : RD->fields()) { // This checker only cares about the padded size of the // field, and not the data size. If the field is a record // with tail padding, then we won't put that number in our From eb1481c101cda91cc11a9a2b16de1db731fa3bff Mon Sep 17 00:00:00 2001 From: Chih-Hung Hsieh Date: Wed, 23 Mar 2016 16:14:12 +0000 Subject: [PATCH 331/742] [analyzer] Fix typo s/initalize/initialize/ Differential Revision: http://reviews.llvm.org/D18363 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@264164 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 7bebf4807d897a856c04c121ac3fd44e06e282fb) --- lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp index 14590837699..5126716fcde 100644 --- a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp @@ -313,7 +313,7 @@ void CallAndMessageChecker::checkPreStmt(const CallExpr *CE, if (L.isUndef()) { if (!BT_call_undef) BT_call_undef.reset(new BuiltinBug( - this, "Called function pointer is an uninitalized pointer value")); + this, "Called function pointer is an uninitialized pointer value")); emitBadCall(BT_call_undef.get(), C, Callee); return; } From 5661ff6182cf51f70e2f548bbffa9f4dd1d3c6a5 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Mon, 28 Mar 2016 20:30:25 +0000 Subject: [PATCH 332/742] [analyzer] Nullability: Don't warn along paths where null returned from non-null. Change the nullability checker to not warn along paths where null is returned from a method with a non-null return type, even when the diagnostic for this return has been suppressed. This prevents warning from methods with non-null return types that inline methods that themselves return nil but that suppressed the diagnostic. Also change the PreconditionViolated state component to be called "InvariantViolated" because it is set when a post-condition is violated, as well. rdar://problem/25393539 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@264647 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 029eb2219ef2ed847e8fcd860070d239a5a8273b) --- .../Checkers/NullabilityChecker.cpp | 134 +++++++++++------- .../system-header-simulator-for-nullability.h | 5 +- test/Analysis/nullability-no-arc.mm | 55 +++++++ 3 files changed, 137 insertions(+), 57 deletions(-) diff --git a/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp index 31560f2687c..35620d33acf 100644 --- a/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp @@ -168,11 +168,11 @@ class NullabilityChecker /// /// When \p SuppressPath is set to true, no more bugs will be reported on this /// path by this checker. - void reportBugIfPreconditionHolds(StringRef Msg, ErrorKind Error, - ExplodedNode *N, const MemRegion *Region, - CheckerContext &C, - const Stmt *ValueExpr = nullptr, - bool SuppressPath = false) const; + void reportBugIfInvariantHolds(StringRef Msg, ErrorKind Error, + ExplodedNode *N, const MemRegion *Region, + CheckerContext &C, + const Stmt *ValueExpr = nullptr, + bool SuppressPath = false) const; void reportBug(StringRef Msg, ErrorKind Error, ExplodedNode *N, const MemRegion *Region, BugReporter &BR, @@ -247,12 +247,31 @@ bool operator==(NullabilityState Lhs, NullabilityState Rhs) { REGISTER_MAP_WITH_PROGRAMSTATE(NullabilityMap, const MemRegion *, NullabilityState) -// If the nullability precondition of a function is violated, we should not -// report nullability related issues on that path. For this reason once a -// precondition is not met on a path, this checker will be esentially turned off -// for the rest of the analysis. We do not want to generate a sink node however, -// so this checker would not lead to reduced coverage. -REGISTER_TRAIT_WITH_PROGRAMSTATE(PreconditionViolated, bool) +// We say "the nullability type invariant is violated" when a location with a +// non-null type contains NULL or a function with a non-null return type returns +// NULL. Violations of the nullability type invariant can be detected either +// directly (for example, when NULL is passed as an argument to a nonnull +// parameter) or indirectly (for example, when, inside a function, the +// programmer defensively checks whether a nonnull parameter contains NULL and +// finds that it does). +// +// As a matter of policy, the nullability checker typically warns on direct +// violations of the nullability invariant (although it uses various +// heuristics to suppress warnings in some cases) but will not warn if the +// invariant has already been violated along the path (either directly or +// indirectly). As a practical matter, this prevents the analyzer from +// (1) warning on defensive code paths where a nullability precondition is +// determined to have been violated, (2) warning additional times after an +// initial direct violation has been discovered, and (3) warning after a direct +// violation that has been implicitly or explicitly suppressed (for +// example, with a cast of NULL to _Nonnull). In essence, once an invariant +// violation is detected on a path, this checker will be esentially turned off +// for the rest of the analysis +// +// The analyzer takes this approach (rather than generating a sink node) to +// ensure coverage of defensive paths, which may be important for backwards +// compatibility in codebases that were developed without nullability in mind. +REGISTER_TRAIT_WITH_PROGRAMSTATE(InvariantViolated, bool) enum class NullConstraint { IsNull, IsNotNull, Unknown }; @@ -366,9 +385,9 @@ checkParamsForPreconditionViolation(const ParamVarDeclRange &Params, return false; } -static bool checkPreconditionViolation(ProgramStateRef State, ExplodedNode *N, - CheckerContext &C) { - if (State->get()) +static bool checkInvariantViolation(ProgramStateRef State, ExplodedNode *N, + CheckerContext &C) { + if (State->get()) return true; const LocationContext *LocCtxt = C.getLocationContext(); @@ -388,21 +407,21 @@ static bool checkPreconditionViolation(ProgramStateRef State, ExplodedNode *N, if (checkParamsForPreconditionViolation(Params, State, LocCtxt)) { if (!N->isSink()) - C.addTransition(State->set(true), N); + C.addTransition(State->set(true), N); return true; } return false; } -void NullabilityChecker::reportBugIfPreconditionHolds(StringRef Msg, +void NullabilityChecker::reportBugIfInvariantHolds(StringRef Msg, ErrorKind Error, ExplodedNode *N, const MemRegion *Region, CheckerContext &C, const Stmt *ValueExpr, bool SuppressPath) const { ProgramStateRef OriginalState = N->getState(); - if (checkPreconditionViolation(OriginalState, N, C)) + if (checkInvariantViolation(OriginalState, N, C)) return; if (SuppressPath) { - OriginalState = OriginalState->set(true); + OriginalState = OriginalState->set(true); N = C.addTransition(OriginalState, N); } @@ -430,7 +449,7 @@ void NullabilityChecker::checkDeadSymbols(SymbolReaper &SR, // preconditions are violated. It is not enough to check this only when we // actually report an error, because at that time interesting symbols might be // reaped. - if (checkPreconditionViolation(State, C.getPredecessor(), C)) + if (checkInvariantViolation(State, C.getPredecessor(), C)) return; C.addTransition(State); } @@ -439,7 +458,7 @@ void NullabilityChecker::checkDeadSymbols(SymbolReaper &SR, /// not know anything about the value of that pointer. When that pointer is /// nullable, this code emits a warning. void NullabilityChecker::checkEvent(ImplicitNullDerefEvent Event) const { - if (Event.SinkNode->getState()->get()) + if (Event.SinkNode->getState()->get()) return; const MemRegion *Region = @@ -486,10 +505,6 @@ static const Expr *lookThroughImplicitCasts(const Expr *E) { /// This method check when nullable pointer or null value is returned from a /// function that has nonnull return type. -/// -/// TODO: when nullability preconditons are violated, it is ok to violate the -/// nullability postconditons (i.e.: when one of the nonnull parameters are null -/// this check should not report any nullability related issue). void NullabilityChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const { auto RetExpr = S->getRetValue(); @@ -500,7 +515,7 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S, return; ProgramStateRef State = C.getState(); - if (State->get()) + if (State->get()) return; auto RetSVal = @@ -542,10 +557,11 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S, Nullability RetExprTypeLevelNullability = getNullabilityAnnotation(lookThroughImplicitCasts(RetExpr)->getType()); + bool NullReturnedFromNonNull = (RequiredNullability == Nullability::Nonnull && + Nullness == NullConstraint::IsNull); if (Filter.CheckNullReturnedFromNonnull && - Nullness == NullConstraint::IsNull && + NullReturnedFromNonNull && RetExprTypeLevelNullability != Nullability::Nonnull && - RequiredNullability == Nullability::Nonnull && !InSuppressedMethodFamily) { static CheckerProgramPointTag Tag(this, "NullReturnedFromNonnull"); ExplodedNode *N = C.generateErrorNode(State, &Tag); @@ -557,9 +573,17 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S, OS << "Null is returned from a " << C.getDeclDescription(D) << " that is expected to return a non-null value"; - reportBugIfPreconditionHolds(OS.str(), - ErrorKind::NilReturnedToNonnull, N, nullptr, C, - RetExpr); + reportBugIfInvariantHolds(OS.str(), + ErrorKind::NilReturnedToNonnull, N, nullptr, C, + RetExpr); + return; + } + + // If null was returned from a non-null function, mark the nullability + // invariant as violated even if the diagnostic was suppressed. + if (NullReturnedFromNonNull) { + State = State->set(true); + C.addTransition(State); return; } @@ -583,9 +607,9 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S, OS << "Nullable pointer is returned from a " << C.getDeclDescription(D) << " that is expected to return a non-null value"; - reportBugIfPreconditionHolds(OS.str(), - ErrorKind::NullableReturnedToNonnull, N, - Region, C); + reportBugIfInvariantHolds(OS.str(), + ErrorKind::NullableReturnedToNonnull, N, + Region, C); } return; } @@ -605,7 +629,7 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call, return; ProgramStateRef State = C.getState(); - if (State->get()) + if (State->get()) return; ProgramStateRef OrigState = State; @@ -646,9 +670,9 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call, llvm::raw_svector_ostream OS(SBuf); OS << "Null passed to a callee that requires a non-null " << ParamIdx << llvm::getOrdinalSuffix(ParamIdx) << " parameter"; - reportBugIfPreconditionHolds(OS.str(), ErrorKind::NilPassedToNonnull, N, - nullptr, C, - ArgExpr, /*SuppressPath=*/false); + reportBugIfInvariantHolds(OS.str(), ErrorKind::NilPassedToNonnull, N, + nullptr, C, + ArgExpr, /*SuppressPath=*/false); return; } @@ -672,17 +696,17 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call, llvm::raw_svector_ostream OS(SBuf); OS << "Nullable pointer is passed to a callee that requires a non-null " << ParamIdx << llvm::getOrdinalSuffix(ParamIdx) << " parameter"; - reportBugIfPreconditionHolds(OS.str(), - ErrorKind::NullablePassedToNonnull, N, - Region, C, ArgExpr, /*SuppressPath=*/true); + reportBugIfInvariantHolds(OS.str(), + ErrorKind::NullablePassedToNonnull, N, + Region, C, ArgExpr, /*SuppressPath=*/true); return; } if (Filter.CheckNullableDereferenced && Param->getType()->isReferenceType()) { ExplodedNode *N = C.addTransition(State); - reportBugIfPreconditionHolds("Nullable pointer is dereferenced", - ErrorKind::NullableDereferenced, N, Region, - C, ArgExpr, /*SuppressPath=*/true); + reportBugIfInvariantHolds("Nullable pointer is dereferenced", + ErrorKind::NullableDereferenced, N, Region, + C, ArgExpr, /*SuppressPath=*/true); return; } continue; @@ -713,7 +737,7 @@ void NullabilityChecker::checkPostCall(const CallEvent &Call, if (!ReturnType->isAnyPointerType()) return; ProgramStateRef State = C.getState(); - if (State->get()) + if (State->get()) return; const MemRegion *Region = getTrackRegion(Call.getReturnValue()); @@ -782,7 +806,7 @@ void NullabilityChecker::checkPostObjCMessage(const ObjCMethodCall &M, return; ProgramStateRef State = C.getState(); - if (State->get()) + if (State->get()) return; const MemRegion *ReturnRegion = getTrackRegion(M.getReturnValue()); @@ -897,7 +921,7 @@ void NullabilityChecker::checkPostStmt(const ExplicitCastExpr *CE, return; ProgramStateRef State = C.getState(); - if (State->get()) + if (State->get()) return; Nullability DestNullability = getNullabilityAnnotation(DestType); @@ -1022,7 +1046,7 @@ void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S, return; ProgramStateRef State = C.getState(); - if (State->get()) + if (State->get()) return; auto ValDefOrUnknown = V.getAs(); @@ -1050,10 +1074,10 @@ void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S, if (!ValueExpr) ValueExpr = S; - reportBugIfPreconditionHolds("Null is assigned to a pointer which is " - "expected to have non-null value", - ErrorKind::NilAssignedToNonnull, N, nullptr, C, - ValueExpr); + reportBugIfInvariantHolds("Null is assigned to a pointer which is " + "expected to have non-null value", + ErrorKind::NilAssignedToNonnull, N, nullptr, C, + ValueExpr); return; } // Intentionally missing case: '0' is bound to a reference. It is handled by @@ -1074,10 +1098,10 @@ void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S, LocNullability == Nullability::Nonnull) { static CheckerProgramPointTag Tag(this, "NullablePassedToNonnull"); ExplodedNode *N = C.addTransition(State, C.getPredecessor(), &Tag); - reportBugIfPreconditionHolds("Nullable pointer is assigned to a pointer " - "which is expected to have non-null value", - ErrorKind::NullableAssignedToNonnull, N, - ValueRegion, C); + reportBugIfInvariantHolds("Nullable pointer is assigned to a pointer " + "which is expected to have non-null value", + ErrorKind::NullableAssignedToNonnull, N, + ValueRegion, C); } return; } diff --git a/test/Analysis/Inputs/system-header-simulator-for-nullability.h b/test/Analysis/Inputs/system-header-simulator-for-nullability.h index 9bb2786bd7f..8d49f323bc1 100644 --- a/test/Analysis/Inputs/system-header-simulator-for-nullability.h +++ b/test/Analysis/Inputs/system-header-simulator-for-nullability.h @@ -11,8 +11,9 @@ NS_ASSUME_NONNULL_BEGIN typedef struct _NSZone NSZone; @protocol NSObject -+ (id)alloc; -- (id)init; ++ (instancetype)alloc; +- (instancetype)init; +- (instancetype)autorelease; @end @protocol NSCopying diff --git a/test/Analysis/nullability-no-arc.mm b/test/Analysis/nullability-no-arc.mm index c0e693e90b3..37d29b7457a 100644 --- a/test/Analysis/nullability-no-arc.mm +++ b/test/Analysis/nullability-no-arc.mm @@ -5,6 +5,8 @@ @protocol NSObject + (id)alloc; - (id)init; +- (instancetype)autorelease; +- (void)release; @end __attribute__((objc_root_class)) @@ -43,3 +45,56 @@ void testObjCNonARCNoInitialization(TestObject * _Nonnull p) { void testObjCNonARCExplicitZeroInitialization() { TestObject * _Nonnull explicitlyZeroInitialized = nil; // expected-warning {{Null is assigned to a pointer which is expected to have non-null value}} } + +@interface ClassWithInitializers : NSObject +@end + +@implementation ClassWithInitializers +- (instancetype _Nonnull)initWithNonnullReturnAndSelfCheckingIdiom { + // This defensive check is a common-enough idiom that we don't want + // to issue a diagnostic for it. + if (self = [super init]) { + } + + return self; // no-warning +} + +- (instancetype _Nonnull)initWithNonnullReturnAndNilReturnViaLocal { + self = [super init]; + // This leaks, but we're not checking for that here. + + ClassWithInitializers *other = nil; + // False negative. Once we have more subtle suppression of defensive checks in + // initializers we should warn here. + return other; +} + +- (instancetype _Nonnull)initWithPreconditionViolation:(int)p { + self = [super init]; + if (p < 0) { + [self release]; + return (ClassWithInitializers * _Nonnull)nil; + } + return self; +} + ++ (instancetype _Nonnull)factoryCallingInitWithNonnullReturnAndSelfCheckingIdiom { + return [[[self alloc] initWithNonnullReturnAndSelfCheckingIdiom] autorelease]; // no-warning +} + ++ (instancetype _Nonnull)factoryCallingInitWithNonnullReturnAndNilReturnViaLocal { + return [[[self alloc] initWithNonnullReturnAndNilReturnViaLocal] autorelease]; // no-warning +} + ++ (instancetype _Nonnull)initWithPreconditionViolation:(int) p { + return [[[self alloc] initWithPreconditionViolation:p] autorelease]; // no-warning +} + +- (TestObject * _Nonnull) returnsNil { + return (TestObject * _Nonnull)nil; +} +- (TestObject * _Nonnull) inlineOfReturnsNilObjCInstanceDirectlyWithSuppressingCast { + TestObject *o = [self returnsNil]; + return o; +} +@end From c3d38149bedfc91a81906fe6861cec2a40a4c64a Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Mon, 28 Mar 2016 23:55:58 +0000 Subject: [PATCH 333/742] [analyzer] Use BodyFarm-synthesized body even when actual body available. Change body autosynthesis to use the BodyFarm-synthesized body even when an actual body exists. This enables the analyzer to use the simpler, analyzer-provided body to model the behavior of the function rather than trying to understand the actual body. Further, this makes the analyzer robust against changes in headers that expose the implementations of those bodies. rdar://problem/25145950 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@264687 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit c2685f09703167912371d2663b35ca6afa3db24d) --- lib/Analysis/AnalysisDeclContext.cpp | 18 ++++++++++++------ test/Analysis/NSString.m | 15 ++++++++++++++- test/Analysis/properties.m | 19 +++++++++++++++++++ 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/lib/Analysis/AnalysisDeclContext.cpp b/lib/Analysis/AnalysisDeclContext.cpp index fe8021b7966..6bbe8f86d48 100644 --- a/lib/Analysis/AnalysisDeclContext.cpp +++ b/lib/Analysis/AnalysisDeclContext.cpp @@ -94,19 +94,25 @@ Stmt *AnalysisDeclContext::getBody(bool &IsAutosynthesized) const { IsAutosynthesized = false; if (const FunctionDecl *FD = dyn_cast(D)) { Stmt *Body = FD->getBody(); - if (!Body && Manager && Manager->synthesizeBodies()) { - Body = getBodyFarm(getASTContext(), Manager->Injector.get()).getBody(FD); - if (Body) + if (Manager && Manager->synthesizeBodies()) { + Stmt *SynthesizedBody = + getBodyFarm(getASTContext(), Manager->Injector.get()).getBody(FD); + if (SynthesizedBody) { + Body = SynthesizedBody; IsAutosynthesized = true; + } } return Body; } else if (const ObjCMethodDecl *MD = dyn_cast(D)) { Stmt *Body = MD->getBody(); - if (!Body && Manager && Manager->synthesizeBodies()) { - Body = getBodyFarm(getASTContext(), Manager->Injector.get()).getBody(MD); - if (Body) + if (Manager && Manager->synthesizeBodies()) { + Stmt *SynthesizedBody = + getBodyFarm(getASTContext(), Manager->Injector.get()).getBody(MD); + if (SynthesizedBody) { + Body = SynthesizedBody; IsAutosynthesized = true; + } } return Body; } else if (const BlockDecl *BD = dyn_cast(D)) diff --git a/test/Analysis/NSString.m b/test/Analysis/NSString.m index e3900334831..799f813022e 100644 --- a/test/Analysis/NSString.m +++ b/test/Analysis/NSString.m @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -triple i386-apple-darwin10 -analyze -analyzer-checker=core,osx.cocoa.NilArg,osx.cocoa.RetainCount,alpha.core -analyzer-store=region -analyzer-constraints=range -verify -Wno-objc-root-class %s // RUN: %clang_cc1 -triple i386-apple-darwin10 -analyze -analyzer-checker=core,osx.cocoa.NilArg,osx.cocoa.RetainCount,alpha.core -analyzer-store=region -analyzer-constraints=range -analyzer-config mode=shallow -verify -Wno-objc-root-class %s // RUN: %clang_cc1 -DTEST_64 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core,osx.cocoa.NilArg,osx.cocoa.RetainCount,alpha.core -analyzer-store=region -analyzer-constraints=range -verify -Wno-objc-root-class %s - +// RUN: %clang_cc1 -DOSATOMIC_USE_INLINED -triple i386-apple-darwin10 -analyze -analyzer-checker=core,osx.cocoa.NilArg,osx.cocoa.RetainCount,alpha.core -analyzer-store=region -analyzer-constraints=range -verify -Wno-objc-root-class %s //===----------------------------------------------------------------------===// // The following code is reduced using delta-debugging from @@ -279,9 +279,22 @@ id testSharedClassFromFunction() { return [[SharedClass alloc] _init]; // no-warning } +#if !(defined(OSATOMIC_USE_INLINED) && OSATOMIC_USE_INLINED) // Test OSCompareAndSwap _Bool OSAtomicCompareAndSwapPtr( void *__oldValue, void *__newValue, void * volatile *__theValue ); extern BOOL objc_atomicCompareAndSwapPtr(id predicate, id replacement, volatile id *objectLocation); +#else +// Test that the body farm models are still used even when a body is available. +_Bool opaque_OSAtomicCompareAndSwapPtr( void *__oldValue, void *__newValue, void * volatile *__theValue ); +_Bool OSAtomicCompareAndSwapPtr( void *__oldValue, void *__newValue, void * volatile *__theValue ) { + return opaque_OSAtomicCompareAndSwapPtr(__oldValue, __newValue, __theValue); +} + +extern BOOL opaque_objc_atomicCompareAndSwapPtr(id predicate, id replacement, volatile id *objectLocation); +extern BOOL objc_atomicCompareAndSwapPtr(id predicate, id replacement, volatile id *objectLocation) { + return opaque_objc_atomicCompareAndSwapPtr(predicate, replacement, objectLocation); +} +#endif void testOSCompareAndSwap() { NSString *old = 0; diff --git a/test/Analysis/properties.m b/test/Analysis/properties.m index bda2149409c..d79643f8b60 100644 --- a/test/Analysis/properties.m +++ b/test/Analysis/properties.m @@ -247,6 +247,25 @@ - (void)testSynthesisForShadowedReadWriteProperties; { } @end +@interface ClassWithSynthesizedPropertyAndGetter +@property (readonly) int someProp; +@end + +@implementation ClassWithSynthesizedPropertyAndGetter +@synthesize someProp; + +// Make sure that the actual getter is inlined and not a getter created +// by BodyFarm +- (void)testBodyFarmGetterNotUsed { + int i = self.someProp; + clang_analyzer_eval(i == 22); // expected-warning {{TRUE}} +} + +-(int)someProp { + return 22; +} +@end + //------ // Setter ivar invalidation. //------ From 4fe5bdc9f0e01267b3b83ffbaf28881ab1c620c6 Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes Date: Wed, 30 Mar 2016 00:25:57 +0000 Subject: [PATCH 334/742] [Driver] Quote clang full version in dwarf producer when invoking cc1as Convenience to allow easy copy-n-paste from clang -v output when reproducing cc1as comandline. rdar://problem/23959295 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@264813 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 6484b95d634f53dd929c75265ef3c4decf397584) --- lib/Driver/Tools.cpp | 3 ++- test/Misc/cc1as-asm.s | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index c8ac070da10..22a50b290eb 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -6086,7 +6086,8 @@ void ClangAs::ConstructJob(Compilation &C, const JobAction &JA, // Set the AT_producer to the clang version when using the integrated // assembler on assembly source files. CmdArgs.push_back("-dwarf-debug-producer"); - CmdArgs.push_back(Args.MakeArgString(getClangFullVersion())); + std::string QuotedClangVersion("'" + getClangFullVersion() + "'"); + CmdArgs.push_back(Args.MakeArgString(QuotedClangVersion)); // And pass along -I options Args.AddAllArgs(CmdArgs, options::OPT_I); diff --git a/test/Misc/cc1as-asm.s b/test/Misc/cc1as-asm.s index af92644073a..36549c020e1 100644 --- a/test/Misc/cc1as-asm.s +++ b/test/Misc/cc1as-asm.s @@ -1,3 +1,5 @@ // Run cc1as asm output path just to make sure it works // REQUIRES: x86-registered-target // RUN: %clang -cc1as -triple x86_64-apple-macosx10.10.0 -filetype asm %s -o /dev/null +// Test that cc1as is able to consume a quoted clang full version +// RUN: %clang -cc1as -triple x86_64-apple-macosx10.10.0 -dwarf-debug-producer 'clang dummy version' -filetype asm %s -o /dev/null From cd3e112cbce860d861218bf1b8feee0251961e96 Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes Date: Tue, 29 Mar 2016 17:35:02 +0000 Subject: [PATCH 335/742] [Sema] Handle UTF-8 invalid format string specifiers Improve invalid format string specifier handling by printing out invalid specifiers characters with \x, \u and \U. Previously clang would print gargabe whenever the character is unprintable. Example, before: NSLog(@"%\u25B9"); => warning: invalid conversion specifier ' [-Wformat-invalid-specifier] after: NSLog(@"%\u25B9"); => warning: invalid conversion specifier '\u25b9' [-Wformat-invalid-specifier] Differential Revision: http://reviews.llvm.org/D18296 rdar://problem/24672159 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@264752 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit ef6fa17f79523b7547dbf5b2dc5c049b26f687af) --- .../clang/Analysis/Analyses/FormatString.h | 8 +--- lib/Analysis/FormatString.cpp | 23 ++++++++++ lib/Analysis/FormatStringParsing.h | 8 +++- lib/Analysis/PrintfFormatString.cpp | 7 ++- lib/Analysis/ScanfFormatString.cpp | 11 +++-- lib/Sema/SemaChecking.cpp | 43 +++++++++++++++--- test/SemaObjC/format-strings-utf8.m | 45 +++++++++++++++++++ 7 files changed, 127 insertions(+), 18 deletions(-) create mode 100644 test/SemaObjC/format-strings-utf8.m diff --git a/include/clang/Analysis/Analyses/FormatString.h b/include/clang/Analysis/Analyses/FormatString.h index 4471311a339..a593e9853e6 100644 --- a/include/clang/Analysis/Analyses/FormatString.h +++ b/include/clang/Analysis/Analyses/FormatString.h @@ -210,6 +210,7 @@ class ConversionSpecifier { unsigned getLength() const { return EndScanList ? EndScanList - Position : 1; } + void setEndScanList(const char *pos) { EndScanList = pos; } bool isIntArg() const { return (kind >= IntArgBeg && kind <= IntArgEnd) || kind == FreeBSDrArg || kind == FreeBSDyArg; } @@ -413,11 +414,6 @@ class PrintfConversionSpecifier : bool isObjCArg() const { return kind >= ObjCBeg && kind <= ObjCEnd; } bool isDoubleArg() const { return kind >= DoubleArgBeg && kind <= DoubleArgEnd; } - unsigned getLength() const { - // Conversion specifiers currently only are represented by - // single characters, but we be flexible. - return 1; - } static bool classof(const analyze_format_string::ConversionSpecifier *CS) { return CS->isPrintfKind(); @@ -546,8 +542,6 @@ class ScanfConversionSpecifier : ScanfConversionSpecifier(const char *pos, Kind k) : ConversionSpecifier(false, pos, k) {} - void setEndScanList(const char *pos) { EndScanList = pos; } - static bool classof(const analyze_format_string::ConversionSpecifier *CS) { return !CS->isPrintfKind(); } diff --git a/lib/Analysis/FormatString.cpp b/lib/Analysis/FormatString.cpp index 0948bc0b08a..a8a911a5b24 100644 --- a/lib/Analysis/FormatString.cpp +++ b/lib/Analysis/FormatString.cpp @@ -15,6 +15,7 @@ #include "FormatStringParsing.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/TargetInfo.h" +#include "llvm/Support/ConvertUTF.h" using clang::analyze_format_string::ArgType; using clang::analyze_format_string::FormatStringHandler; @@ -252,6 +253,28 @@ clang::analyze_format_string::ParseLengthModifier(FormatSpecifier &FS, return true; } +bool clang::analyze_format_string::ParseUTF8InvalidSpecifier( + const char *SpecifierBegin, const char *FmtStrEnd, unsigned &Len) { + if (SpecifierBegin + 1 >= FmtStrEnd) + return false; + + const UTF8 *SB = reinterpret_cast(SpecifierBegin + 1); + const UTF8 *SE = reinterpret_cast(FmtStrEnd); + const char FirstByte = *SB; + + // If the invalid specifier is a multibyte UTF-8 string, return the + // total length accordingly so that the conversion specifier can be + // properly updated to reflect a complete UTF-8 specifier. + unsigned NumBytes = getNumBytesForUTF8(FirstByte); + if (NumBytes == 1) + return false; + if (SB + NumBytes > SE) + return false; + + Len = NumBytes + 1; + return true; +} + //===----------------------------------------------------------------------===// // Methods on ArgType. //===----------------------------------------------------------------------===// diff --git a/lib/Analysis/FormatStringParsing.h b/lib/Analysis/FormatStringParsing.h index e1652964b8c..8463fcec5bf 100644 --- a/lib/Analysis/FormatStringParsing.h +++ b/lib/Analysis/FormatStringParsing.h @@ -46,7 +46,13 @@ bool ParseArgPosition(FormatStringHandler &H, /// FormatSpecifier& argument, and false otherwise. bool ParseLengthModifier(FormatSpecifier &FS, const char *&Beg, const char *E, const LangOptions &LO, bool IsScanf = false); - + +/// Returns true if the invalid specifier in \p SpecifierBegin is a UTF-8 +/// string; check that it won't go further than \p FmtStrEnd and write +/// up the total size in \p Len. +bool ParseUTF8InvalidSpecifier(const char *SpecifierBegin, + const char *FmtStrEnd, unsigned &Len); + template class SpecifierResult { T FS; const char *Start; diff --git a/lib/Analysis/PrintfFormatString.cpp b/lib/Analysis/PrintfFormatString.cpp index f0976bce972..fb5df61c5ed 100644 --- a/lib/Analysis/PrintfFormatString.cpp +++ b/lib/Analysis/PrintfFormatString.cpp @@ -312,8 +312,13 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H, argIndex++; if (k == ConversionSpecifier::InvalidSpecifier) { + unsigned Len = I - Start; + if (ParseUTF8InvalidSpecifier(Start, E, Len)) { + CS.setEndScanList(Start + Len); + FS.setConversionSpecifier(CS); + } // Assume the conversion takes one argument. - return !H.HandleInvalidPrintfConversionSpecifier(FS, Start, I - Start); + return !H.HandleInvalidPrintfConversionSpecifier(FS, Start, Len); } return PrintfSpecifierResult(Start, FS); } diff --git a/lib/Analysis/ScanfFormatString.cpp b/lib/Analysis/ScanfFormatString.cpp index d484d8e828c..82b038864c2 100644 --- a/lib/Analysis/ScanfFormatString.cpp +++ b/lib/Analysis/ScanfFormatString.cpp @@ -79,7 +79,7 @@ static ScanfSpecifierResult ParseScanfSpecifier(FormatStringHandler &H, unsigned &argIndex, const LangOptions &LO, const TargetInfo &Target) { - + using namespace clang::analyze_format_string; using namespace clang::analyze_scanf; const char *I = Beg; const char *Start = nullptr; @@ -210,10 +210,15 @@ static ScanfSpecifierResult ParseScanfSpecifier(FormatStringHandler &H, // FIXME: '%' and '*' doesn't make sense. Issue a warning. // FIXME: 'ConsumedSoFar' and '*' doesn't make sense. - + if (k == ScanfConversionSpecifier::InvalidSpecifier) { + unsigned Len = I - Beg; + if (ParseUTF8InvalidSpecifier(Beg, E, Len)) { + CS.setEndScanList(Beg + Len); + FS.setConversionSpecifier(CS); + } // Assume the conversion takes one argument. - return !H.HandleInvalidScanfConversionSpecifier(FS, Beg, I - Beg); + return !H.HandleInvalidScanfConversionSpecifier(FS, Beg, Len); } return ScanfSpecifierResult(Start, FS); } diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index 5fa6b640e82..e56483133df 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -36,6 +36,8 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallBitVector.h" #include "llvm/ADT/SmallString.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/Locale.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/raw_ostream.h" #include @@ -3613,12 +3615,41 @@ CheckFormatHandler::HandleInvalidConversionSpecifier(unsigned argIndex, // gibberish when trying to match arguments. keepGoing = false; } - - EmitFormatDiagnostic(S.PDiag(diag::warn_format_invalid_conversion) - << StringRef(csStart, csLen), - Loc, /*IsStringLocation*/true, - getSpecifierRange(startSpec, specifierLen)); - + + StringRef Specifier(csStart, csLen); + + // If the specifier in non-printable, it could be the first byte of a UTF-8 + // sequence. In that case, print the UTF-8 code point. If not, print the byte + // hex value. + std::string CodePointStr; + if (!llvm::sys::locale::isPrint(*csStart)) { + UTF32 CodePoint; + const UTF8 **B = reinterpret_cast(&csStart); + const UTF8 *E = + reinterpret_cast(csStart + csLen); + ConversionResult Result = + llvm::convertUTF8Sequence(B, E, &CodePoint, strictConversion); + + if (Result != conversionOK) { + unsigned char FirstChar = *csStart; + CodePoint = (UTF32)FirstChar; + } + + llvm::raw_string_ostream OS(CodePointStr); + if (CodePoint < 256) + OS << "\\x" << llvm::format("%02x", CodePoint); + else if (CodePoint <= 0xFFFF) + OS << "\\u" << llvm::format("%04x", CodePoint); + else + OS << "\\U" << llvm::format("%08x", CodePoint); + OS.flush(); + Specifier = CodePointStr; + } + + EmitFormatDiagnostic( + S.PDiag(diag::warn_format_invalid_conversion) << Specifier, Loc, + /*IsStringLocation*/ true, getSpecifierRange(startSpec, specifierLen)); + return keepGoing; } diff --git a/test/SemaObjC/format-strings-utf8.m b/test/SemaObjC/format-strings-utf8.m new file mode 100644 index 00000000000..d4c21b11ea7 --- /dev/null +++ b/test/SemaObjC/format-strings-utf8.m @@ -0,0 +1,45 @@ +// REQUIRES: system-darwin +// RUN: rm -f %t.log +// RUN: env RC_DEBUG_OPTIONS=1 \ +// RUN: CC_LOG_DIAGNOSTICS=1 CC_LOG_DIAGNOSTICS_FILE=%t.log \ +// RUN: %clang -target x86_64-apple-darwin -fsyntax-only %s +// RUN: FileCheck %s < %t.log + +#include +int printf(const char *restrict, ...); +int scanf(const char * restrict, ...); +@class NSString, Protocol; +extern void NSLog(NSString *format, ...); + +void testInvalidNoPrintable(int *a) { + // CHECK: invalid conversion specifier '\u25b9' + // CHECK: invalid conversion specifier '\u25b9' + // CHECK: invalid conversion specifier '\U00010348' + // CHECK: invalid conversion specifier '\U00010348' + // CHECK: invalid conversion specifier '\xe2' + // CHECK: invalid conversion specifier '\u25b9' + // CHECK: invalid conversion specifier '\u25b9' + // CHECK: invalid conversion specifier '\U00010348' + // CHECK: invalid conversion specifier '\U00010348' + // CHECK: invalid conversion specifier '\xe2' + // CHECK: invalid conversion specifier '\u25b9' + // CHECK: invalid conversion specifier '\u25b9' + // CHECK: invalid conversion specifier '\U00010348' + // CHECK: invalid conversion specifier '\U00010348' + // CHECK: invalid conversion specifier '\xe2' + printf("%\u25B9"); + printf("%\xE2\x96\xB9"); + printf("%\U00010348"); + printf("%\xF0\x90\x8D\x88"); + printf("%\xe2"); + NSLog(@"%\u25B9"); + NSLog(@"%\xE2\x96\xB9"); + NSLog(@"%\U00010348"); + NSLog(@"%\xF0\x90\x8D\x88"); + NSLog(@"%\xe2"); + scanf("%\u25B9", a); + scanf("%\xE2\x96\xB9", a); + scanf("%\U00010348", a); + scanf("%\xF0\x90\x8D\x88", a); + scanf("%\xe2", a); +} From 65bce4a3d398f11c6c87eba7252d6b4e43a79e6e Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Wed, 23 Mar 2016 21:39:31 +0000 Subject: [PATCH 336/742] ObjC: add getter/setter for class properties to global pool. rdar://problem/25323072 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@264196 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/SemaObjCProperty.cpp | 5 +++++ test/SemaObjC/objc-class-property.m | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/lib/Sema/SemaObjCProperty.cpp b/lib/Sema/SemaObjCProperty.cpp index c9d2da880e1..ef7fc6c2089 100644 --- a/lib/Sema/SemaObjCProperty.cpp +++ b/lib/Sema/SemaObjCProperty.cpp @@ -2312,6 +2312,11 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property) { AddInstanceMethodToGlobalPool(GetterMethod); if (SetterMethod) AddInstanceMethodToGlobalPool(SetterMethod); + } else { + if (GetterMethod) + AddFactoryMethodToGlobalPool(GetterMethod); + if (SetterMethod) + AddFactoryMethodToGlobalPool(SetterMethod); } ObjCInterfaceDecl *CurrentClass = dyn_cast(CD); diff --git a/test/SemaObjC/objc-class-property.m b/test/SemaObjC/objc-class-property.m index 0058ee3648b..37a8178ceb6 100644 --- a/test/SemaObjC/objc-class-property.m +++ b/test/SemaObjC/objc-class-property.m @@ -33,3 +33,11 @@ int test() { A *a = [[A alloc] init]; return a.x + A.c; } + +void message_id(id me) { + [me y]; +} + +void message_class(Class me) { + [me c2]; +} From 385d715a621aace0a10fda2a27036acdf4b87abc Mon Sep 17 00:00:00 2001 From: NAKAMURA Takumi Date: Sat, 13 Feb 2016 04:01:49 +0000 Subject: [PATCH 337/742] libclang/CMakeLists.txt: Prune IndexingContext.h out of ADDITIONAL_HEADERS. VS IDE uses it. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260802 91177308-0d34-0410-b5e6-96231b3b80d8 --- tools/libclang/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/libclang/CMakeLists.txt b/tools/libclang/CMakeLists.txt index 0dfad30ac28..20a2db312e6 100644 --- a/tools/libclang/CMakeLists.txt +++ b/tools/libclang/CMakeLists.txt @@ -30,7 +30,6 @@ set(SOURCES CXTranslationUnit.h CXType.h Index_Internal.h - IndexingContext.h ../../include/clang-c/Index.h ) From 622573f9c7f4e45ed58daff62022e6c1a39837a8 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Sun, 7 Feb 2016 18:21:28 +0000 Subject: [PATCH 338/742] [libclang] Add missing CINDEX_LINKAGE from a function. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260047 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang-c/Index.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h index 69a98d7978c..4ecbed5f19a 100644 --- a/include/clang-c/Index.h +++ b/include/clang-c/Index.h @@ -3640,8 +3640,8 @@ typedef enum CXChildVisitResult * Visits the children of a cursor using the specified block. Behaves * identically to clang_visitChildren() in all other respects. */ -unsigned clang_visitChildrenWithBlock(CXCursor parent, - CXCursorVisitorBlock block); +CINDEX_LINKAGE unsigned clang_visitChildrenWithBlock(CXCursor parent, + CXCursorVisitorBlock block); # endif #endif From 48986a79be89bdc25d202a621feca2e287ea117d Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Sun, 7 Feb 2016 19:28:36 +0000 Subject: [PATCH 339/742] [Frontend] Make the memory management of FrontendAction pointers explicit by using unique_ptr. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260048 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/ARCMigrate/ARCMTActions.h | 11 +-- include/clang/Frontend/FrontendAction.h | 2 +- include/clang/Frontend/FrontendActions.h | 5 +- .../clang/Rewrite/Frontend/FrontendActions.h | 4 +- lib/ARCMigrate/ARCMTActions.cpp | 12 +-- lib/ARCMigrate/ObjCMT.cpp | 5 +- lib/Frontend/ASTMerge.cpp | 5 +- lib/Frontend/FrontendAction.cpp | 5 +- .../ExecuteCompilerInvocation.cpp | 89 ++++++++++--------- 9 files changed, 73 insertions(+), 65 deletions(-) diff --git a/include/clang/ARCMigrate/ARCMTActions.h b/include/clang/ARCMigrate/ARCMTActions.h index c830aa3d787..554e0c0c6d0 100644 --- a/include/clang/ARCMigrate/ARCMTActions.h +++ b/include/clang/ARCMigrate/ARCMTActions.h @@ -22,7 +22,7 @@ class CheckAction : public WrapperFrontendAction { bool BeginInvocation(CompilerInstance &CI) override; public: - CheckAction(FrontendAction *WrappedAction); + CheckAction(std::unique_ptr WrappedAction); }; class ModifyAction : public WrapperFrontendAction { @@ -30,7 +30,7 @@ class ModifyAction : public WrapperFrontendAction { bool BeginInvocation(CompilerInstance &CI) override; public: - ModifyAction(FrontendAction *WrappedAction); + ModifyAction(std::unique_ptr WrappedAction); }; class MigrateSourceAction : public ASTFrontendAction { @@ -49,7 +49,8 @@ class MigrateAction : public WrapperFrontendAction { bool BeginInvocation(CompilerInstance &CI) override; public: - MigrateAction(FrontendAction *WrappedAction, StringRef migrateDir, + MigrateAction(std::unique_ptr WrappedAction, + StringRef migrateDir, StringRef plistOut, bool emitPremigrationARCErrors); }; @@ -61,8 +62,8 @@ class ObjCMigrateAction : public WrapperFrontendAction { FileRemapper Remapper; CompilerInstance *CompInst; public: - ObjCMigrateAction(FrontendAction *WrappedAction, StringRef migrateDir, - unsigned migrateAction); + ObjCMigrateAction(std::unique_ptr WrappedAction, + StringRef migrateDir, unsigned migrateAction); protected: std::unique_ptr CreateASTConsumer(CompilerInstance &CI, diff --git a/include/clang/Frontend/FrontendAction.h b/include/clang/Frontend/FrontendAction.h index c407ff80ac5..1b021ef9e9f 100644 --- a/include/clang/Frontend/FrontendAction.h +++ b/include/clang/Frontend/FrontendAction.h @@ -283,7 +283,7 @@ class WrapperFrontendAction : public FrontendAction { public: /// Construct a WrapperFrontendAction from an existing action, taking /// ownership of it. - WrapperFrontendAction(FrontendAction *WrappedAction); + WrapperFrontendAction(std::unique_ptr WrappedAction); bool usesPreprocessorOnly() const override; TranslationUnitKind getTranslationUnitKind() override; diff --git a/include/clang/Frontend/FrontendActions.h b/include/clang/Frontend/FrontendActions.h index f61775f014f..025955dd5ef 100644 --- a/include/clang/Frontend/FrontendActions.h +++ b/include/clang/Frontend/FrontendActions.h @@ -168,7 +168,7 @@ class VerifyPCHAction : public ASTFrontendAction { */ class ASTMergeAction : public FrontendAction { /// \brief The action that the merge action adapts. - FrontendAction *AdaptedAction; + std::unique_ptr AdaptedAction; /// \brief The set of AST files to merge. std::vector ASTFiles; @@ -184,7 +184,8 @@ class ASTMergeAction : public FrontendAction { void EndSourceFileAction() override; public: - ASTMergeAction(FrontendAction *AdaptedAction, ArrayRef ASTFiles); + ASTMergeAction(std::unique_ptr AdaptedAction, + ArrayRef ASTFiles); ~ASTMergeAction() override; bool usesPreprocessorOnly() const override; diff --git a/include/clang/Rewrite/Frontend/FrontendActions.h b/include/clang/Rewrite/Frontend/FrontendActions.h index 6c290e4d605..27976eac4ed 100644 --- a/include/clang/Rewrite/Frontend/FrontendActions.h +++ b/include/clang/Rewrite/Frontend/FrontendActions.h @@ -50,8 +50,8 @@ class FixItAction : public ASTFrontendAction { /// frontend action. class FixItRecompile : public WrapperFrontendAction { public: - FixItRecompile(FrontendAction *WrappedAction) - : WrapperFrontendAction(WrappedAction) {} + FixItRecompile(std::unique_ptr WrappedAction) + : WrapperFrontendAction(std::move(WrappedAction)) {} protected: bool BeginInvocation(CompilerInstance &CI) override; diff --git a/lib/ARCMigrate/ARCMTActions.cpp b/lib/ARCMigrate/ARCMTActions.cpp index 39a922f426c..0a5473ab19e 100644 --- a/lib/ARCMigrate/ARCMTActions.cpp +++ b/lib/ARCMigrate/ARCMTActions.cpp @@ -25,8 +25,8 @@ bool CheckAction::BeginInvocation(CompilerInstance &CI) { return true; } -CheckAction::CheckAction(FrontendAction *WrappedAction) - : WrapperFrontendAction(WrappedAction) {} +CheckAction::CheckAction(std::unique_ptr WrappedAction) + : WrapperFrontendAction(std::move(WrappedAction)) {} bool ModifyAction::BeginInvocation(CompilerInstance &CI) { return !arcmt::applyTransformations(CI.getInvocation(), getCurrentInput(), @@ -34,8 +34,8 @@ bool ModifyAction::BeginInvocation(CompilerInstance &CI) { CI.getDiagnostics().getClient()); } -ModifyAction::ModifyAction(FrontendAction *WrappedAction) - : WrapperFrontendAction(WrappedAction) {} +ModifyAction::ModifyAction(std::unique_ptr WrappedAction) + : WrapperFrontendAction(std::move(WrappedAction)) {} bool MigrateAction::BeginInvocation(CompilerInstance &CI) { if (arcmt::migrateWithTemporaryFiles( @@ -49,11 +49,11 @@ bool MigrateAction::BeginInvocation(CompilerInstance &CI) { return true; } -MigrateAction::MigrateAction(FrontendAction *WrappedAction, +MigrateAction::MigrateAction(std::unique_ptr WrappedAction, StringRef migrateDir, StringRef plistOut, bool emitPremigrationARCErrors) - : WrapperFrontendAction(WrappedAction), MigrateDir(migrateDir), + : WrapperFrontendAction(std::move(WrappedAction)), MigrateDir(migrateDir), PlistOut(plistOut), EmitPremigrationARCErros(emitPremigrationARCErrors) { if (MigrateDir.empty()) MigrateDir = "."; // user current directory if none is given. diff --git a/lib/ARCMigrate/ObjCMT.cpp b/lib/ARCMigrate/ObjCMT.cpp index 1be724c38af..3737914a7cf 100644 --- a/lib/ARCMigrate/ObjCMT.cpp +++ b/lib/ARCMigrate/ObjCMT.cpp @@ -179,10 +179,11 @@ class ObjCMigrateASTConsumer : public ASTConsumer { } -ObjCMigrateAction::ObjCMigrateAction(FrontendAction *WrappedAction, +ObjCMigrateAction::ObjCMigrateAction( + std::unique_ptr WrappedAction, StringRef migrateDir, unsigned migrateAction) - : WrapperFrontendAction(WrappedAction), MigrateDir(migrateDir), + : WrapperFrontendAction(std::move(WrappedAction)), MigrateDir(migrateDir), ObjCMigAction(migrateAction), CompInst(nullptr) { if (MigrateDir.empty()) diff --git a/lib/Frontend/ASTMerge.cpp b/lib/Frontend/ASTMerge.cpp index b499fa2b0e6..51064da270c 100644 --- a/lib/Frontend/ASTMerge.cpp +++ b/lib/Frontend/ASTMerge.cpp @@ -83,14 +83,13 @@ void ASTMergeAction::EndSourceFileAction() { return AdaptedAction->EndSourceFileAction(); } -ASTMergeAction::ASTMergeAction(FrontendAction *AdaptedAction, +ASTMergeAction::ASTMergeAction(std::unique_ptr adaptedAction, ArrayRef ASTFiles) - : AdaptedAction(AdaptedAction), ASTFiles(ASTFiles.begin(), ASTFiles.end()) { +: AdaptedAction(std::move(adaptedAction)), ASTFiles(ASTFiles.begin(), ASTFiles.end()) { assert(AdaptedAction && "ASTMergeAction needs an action to adapt"); } ASTMergeAction::~ASTMergeAction() { - delete AdaptedAction; } bool ASTMergeAction::usesPreprocessorOnly() const { diff --git a/lib/Frontend/FrontendAction.cpp b/lib/Frontend/FrontendAction.cpp index 005bbf6e274..71fb17c25f5 100644 --- a/lib/Frontend/FrontendAction.cpp +++ b/lib/Frontend/FrontendAction.cpp @@ -594,6 +594,7 @@ bool WrapperFrontendAction::hasCodeCompletionSupport() const { return WrappedAction->hasCodeCompletionSupport(); } -WrapperFrontendAction::WrapperFrontendAction(FrontendAction *WrappedAction) - : WrappedAction(WrappedAction) {} +WrapperFrontendAction::WrapperFrontendAction( + std::unique_ptr WrappedAction) + : WrappedAction(std::move(WrappedAction)) {} diff --git a/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/lib/FrontendTool/ExecuteCompilerInvocation.cpp index 79cf0049a7b..116590e5375 100644 --- a/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -31,33 +31,34 @@ using namespace clang; using namespace llvm::opt; -static FrontendAction *CreateFrontendBaseAction(CompilerInstance &CI) { +static std::unique_ptr +CreateFrontendBaseAction(CompilerInstance &CI) { using namespace clang::frontend; StringRef Action("unknown"); (void)Action; switch (CI.getFrontendOpts().ProgramAction) { - case ASTDeclList: return new ASTDeclListAction(); - case ASTDump: return new ASTDumpAction(); - case ASTPrint: return new ASTPrintAction(); - case ASTView: return new ASTViewAction(); - case DumpRawTokens: return new DumpRawTokensAction(); - case DumpTokens: return new DumpTokensAction(); - case EmitAssembly: return new EmitAssemblyAction(); - case EmitBC: return new EmitBCAction(); - case EmitHTML: return new HTMLPrintAction(); - case EmitLLVM: return new EmitLLVMAction(); - case EmitLLVMOnly: return new EmitLLVMOnlyAction(); - case EmitCodeGenOnly: return new EmitCodeGenOnlyAction(); - case EmitObj: return new EmitObjAction(); - case FixIt: return new FixItAction(); - case GenerateModule: return new GenerateModuleAction; - case GeneratePCH: return new GeneratePCHAction; - case GeneratePTH: return new GeneratePTHAction(); - case InitOnly: return new InitOnlyAction(); - case ParseSyntaxOnly: return new SyntaxOnlyAction(); - case ModuleFileInfo: return new DumpModuleInfoAction(); - case VerifyPCH: return new VerifyPCHAction(); + case ASTDeclList: return llvm::make_unique(); + case ASTDump: return llvm::make_unique(); + case ASTPrint: return llvm::make_unique(); + case ASTView: return llvm::make_unique(); + case DumpRawTokens: return llvm::make_unique(); + case DumpTokens: return llvm::make_unique(); + case EmitAssembly: return llvm::make_unique(); + case EmitBC: return llvm::make_unique(); + case EmitHTML: return llvm::make_unique(); + case EmitLLVM: return llvm::make_unique(); + case EmitLLVMOnly: return llvm::make_unique(); + case EmitCodeGenOnly: return llvm::make_unique(); + case EmitObj: return llvm::make_unique(); + case FixIt: return llvm::make_unique(); + case GenerateModule: return llvm::make_unique(); + case GeneratePCH: return llvm::make_unique(); + case GeneratePTH: return llvm::make_unique(); + case InitOnly: return llvm::make_unique(); + case ParseSyntaxOnly: return llvm::make_unique(); + case ModuleFileInfo: return llvm::make_unique(); + case VerifyPCH: return llvm::make_unique(); case PluginAction: { for (FrontendPluginRegistry::iterator it = @@ -67,7 +68,7 @@ static FrontendAction *CreateFrontendBaseAction(CompilerInstance &CI) { std::unique_ptr P(it->instantiate()); if (!P->ParseArgs(CI, CI.getFrontendOpts().PluginArgs)) return nullptr; - return P.release(); + return std::move(P); } } @@ -76,32 +77,33 @@ static FrontendAction *CreateFrontendBaseAction(CompilerInstance &CI) { return nullptr; } - case PrintDeclContext: return new DeclContextPrintAction(); - case PrintPreamble: return new PrintPreambleAction(); + case PrintDeclContext: return llvm::make_unique(); + case PrintPreamble: return llvm::make_unique(); case PrintPreprocessedInput: { if (CI.getPreprocessorOutputOpts().RewriteIncludes) - return new RewriteIncludesAction(); - return new PrintPreprocessedAction(); + return llvm::make_unique(); + return llvm::make_unique(); } - case RewriteMacros: return new RewriteMacrosAction(); - case RewriteTest: return new RewriteTestAction(); + case RewriteMacros: return llvm::make_unique(); + case RewriteTest: return llvm::make_unique(); #ifdef CLANG_ENABLE_OBJC_REWRITER - case RewriteObjC: return new RewriteObjCAction(); + case RewriteObjC: return llvm::make_unique(); #else case RewriteObjC: Action = "RewriteObjC"; break; #endif #ifdef CLANG_ENABLE_ARCMT - case MigrateSource: return new arcmt::MigrateSourceAction(); + case MigrateSource: + return llvm::make_unique(); #else case MigrateSource: Action = "MigrateSource"; break; #endif #ifdef CLANG_ENABLE_STATIC_ANALYZER - case RunAnalysis: return new ento::AnalysisAction(); + case RunAnalysis: return llvm::make_unique(); #else case RunAnalysis: Action = "RunAnalysis"; break; #endif - case RunPreprocessorOnly: return new PreprocessOnlyAction(); + case RunPreprocessorOnly: return llvm::make_unique(); } #if !defined(CLANG_ENABLE_ARCMT) || !defined(CLANG_ENABLE_STATIC_ANALYZER) \ @@ -113,16 +115,17 @@ static FrontendAction *CreateFrontendBaseAction(CompilerInstance &CI) { #endif } -static FrontendAction *CreateFrontendAction(CompilerInstance &CI) { +static std::unique_ptr +CreateFrontendAction(CompilerInstance &CI) { // Create the underlying action. - FrontendAction *Act = CreateFrontendBaseAction(CI); + std::unique_ptr Act = CreateFrontendBaseAction(CI); if (!Act) return nullptr; const FrontendOptions &FEOpts = CI.getFrontendOpts(); if (FEOpts.FixAndRecompile) { - Act = new FixItRecompile(Act); + Act = llvm::make_unique(std::move(Act)); } #ifdef CLANG_ENABLE_ARCMT @@ -133,13 +136,13 @@ static FrontendAction *CreateFrontendAction(CompilerInstance &CI) { case FrontendOptions::ARCMT_None: break; case FrontendOptions::ARCMT_Check: - Act = new arcmt::CheckAction(Act); + Act = llvm::make_unique(std::move(Act)); break; case FrontendOptions::ARCMT_Modify: - Act = new arcmt::ModifyAction(Act); + Act = llvm::make_unique(std::move(Act)); break; case FrontendOptions::ARCMT_Migrate: - Act = new arcmt::MigrateAction(Act, + Act = llvm::make_unique(std::move(Act), FEOpts.MTMigrateDir, FEOpts.ARCMTMigrateReportOut, FEOpts.ARCMTMigrateEmitARCErrors); @@ -147,8 +150,9 @@ static FrontendAction *CreateFrontendAction(CompilerInstance &CI) { } if (FEOpts.ObjCMTAction != FrontendOptions::ObjCMT_None) { - Act = new arcmt::ObjCMigrateAction(Act, FEOpts.MTMigrateDir, - FEOpts.ObjCMTAction); + Act = llvm::make_unique(std::move(Act), + FEOpts.MTMigrateDir, + FEOpts.ObjCMTAction); } } #endif @@ -156,7 +160,8 @@ static FrontendAction *CreateFrontendAction(CompilerInstance &CI) { // If there are any AST files to merge, create a frontend action // adaptor to perform the merge. if (!FEOpts.ASTMergeFiles.empty()) - Act = new ASTMergeAction(Act, FEOpts.ASTMergeFiles); + Act = llvm::make_unique(std::move(Act), + FEOpts.ASTMergeFiles); return Act; } From 2373b6544cf9b207938531c66fe88ea31b670ee0 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Tue, 9 Feb 2016 19:07:07 +0000 Subject: [PATCH 340/742] [libclang] indexing: Have the semantic container of synthesized ObjC getter/setter methods be the implementation decl. Matches the behavior of other ObjC methods. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260250 91177308-0d34-0410-b5e6-96231b3b80d8 --- tools/libclang/IndexingContext.cpp | 11 +++++++---- tools/libclang/IndexingContext.h | 3 ++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/tools/libclang/IndexingContext.cpp b/tools/libclang/IndexingContext.cpp index f7640c63e05..5d944bae1cb 100644 --- a/tools/libclang/IndexingContext.cpp +++ b/tools/libclang/IndexingContext.cpp @@ -309,7 +309,8 @@ void IndexingContext::handleDiagnosticSet(CXDiagnostic CXDiagSet) { bool IndexingContext::handleDecl(const NamedDecl *D, SourceLocation Loc, CXCursor Cursor, DeclInfo &DInfo, - const DeclContext *LexicalDC) { + const DeclContext *LexicalDC, + const DeclContext *SemaDC) { if (!CB.indexDeclaration || !D) return false; if (D->isImplicit() && shouldIgnoreIfImplicit(D)) @@ -335,10 +336,12 @@ bool IndexingContext::handleDecl(const NamedDecl *D, DInfo.attributes = DInfo.EntInfo.attributes; DInfo.numAttributes = DInfo.EntInfo.numAttributes; - getContainerInfo(D->getDeclContext(), DInfo.SemanticContainer); + if (!SemaDC) + SemaDC = D->getDeclContext(); + getContainerInfo(SemaDC, DInfo.SemanticContainer); DInfo.semanticContainer = &DInfo.SemanticContainer; - if (LexicalDC == D->getDeclContext()) { + if (LexicalDC == SemaDC) { DInfo.lexicalContainer = &DInfo.SemanticContainer; } else if (isTemplateImplicitInstantiation(D)) { // Implicit instantiations have the lexical context of where they were @@ -598,7 +601,7 @@ bool IndexingContext::handleSynthesizedObjCMethod(const ObjCMethodDecl *D, const DeclContext *LexicalDC) { DeclInfo DInfo(/*isRedeclaration=*/true, /*isDefinition=*/true, /*isContainer=*/false); - return handleDecl(D, Loc, getCursor(D), DInfo, LexicalDC); + return handleDecl(D, Loc, getCursor(D), DInfo, LexicalDC, LexicalDC); } bool IndexingContext::handleObjCProperty(const ObjCPropertyDecl *D) { diff --git a/tools/libclang/IndexingContext.h b/tools/libclang/IndexingContext.h index 4da6aebaf64..2b1355ad32a 100644 --- a/tools/libclang/IndexingContext.h +++ b/tools/libclang/IndexingContext.h @@ -468,7 +468,8 @@ class IndexingContext { bool handleDecl(const NamedDecl *D, SourceLocation Loc, CXCursor Cursor, DeclInfo &DInfo, - const DeclContext *LexicalDC = nullptr); + const DeclContext *LexicalDC = nullptr, + const DeclContext *SemaDC = nullptr); bool handleObjCContainer(const ObjCContainerDecl *D, SourceLocation Loc, CXCursor Cursor, From ea70fe1c1a4bf97ccdc6a9b930e1aedf97020287 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Tue, 9 Feb 2016 19:07:13 +0000 Subject: [PATCH 341/742] [ASTUnit] Change the parameter of ASTUnit::LoadFromCompilerInvocationAction to accept a more general FrontendAction. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260251 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Frontend/ASTUnit.h | 4 ++-- lib/Frontend/ASTUnit.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/clang/Frontend/ASTUnit.h b/include/clang/Frontend/ASTUnit.h index a5f7af57143..04e6dce5110 100644 --- a/include/clang/Frontend/ASTUnit.h +++ b/include/clang/Frontend/ASTUnit.h @@ -60,7 +60,7 @@ class PCHContainerOperations; class PCHContainerReader; class SourceManager; class TargetInfo; -class ASTFrontendAction; +class FrontendAction; class ASTDeserializationListener; /// \brief Utility class for loading a ASTContext from an AST file. @@ -781,7 +781,7 @@ class ASTUnit : public ModuleLoader { CompilerInvocation *CI, std::shared_ptr PCHContainerOps, IntrusiveRefCntPtr Diags, - ASTFrontendAction *Action = nullptr, ASTUnit *Unit = nullptr, + FrontendAction *Action = nullptr, ASTUnit *Unit = nullptr, bool Persistent = true, StringRef ResourceFilesPath = StringRef(), bool OnlyLocalDecls = false, bool CaptureDiagnostics = false, unsigned PrecompilePreambleAfterNParses = 0, diff --git a/lib/Frontend/ASTUnit.cpp b/lib/Frontend/ASTUnit.cpp index e6ba29201f8..54ac6048bc1 100644 --- a/lib/Frontend/ASTUnit.cpp +++ b/lib/Frontend/ASTUnit.cpp @@ -1723,7 +1723,7 @@ ASTUnit *ASTUnit::create(CompilerInvocation *CI, ASTUnit *ASTUnit::LoadFromCompilerInvocationAction( CompilerInvocation *CI, std::shared_ptr PCHContainerOps, - IntrusiveRefCntPtr Diags, ASTFrontendAction *Action, + IntrusiveRefCntPtr Diags, FrontendAction *Action, ASTUnit *Unit, bool Persistent, StringRef ResourceFilesPath, bool OnlyLocalDecls, bool CaptureDiagnostics, unsigned PrecompilePreambleAfterNParses, bool CacheCodeCompletionResults, @@ -1812,7 +1812,7 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocationAction( // Create the source manager. Clang->setSourceManager(&AST->getSourceManager()); - ASTFrontendAction *Act = Action; + FrontendAction *Act = Action; std::unique_ptr TrackerAct; if (!Act) { From 3b7fa112b451203561b774e14ce79b3e5d5a03b5 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Tue, 9 Feb 2016 19:07:16 +0000 Subject: [PATCH 342/742] [Frontend] Handle ASTConsumer::shouldSkipFunctionBody via the MultiplexConsumer. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260252 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Frontend/MultiplexConsumer.h | 1 + lib/Frontend/MultiplexConsumer.cpp | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/include/clang/Frontend/MultiplexConsumer.h b/include/clang/Frontend/MultiplexConsumer.h index 97828293387..ae6db29258d 100644 --- a/include/clang/Frontend/MultiplexConsumer.h +++ b/include/clang/Frontend/MultiplexConsumer.h @@ -53,6 +53,7 @@ class MultiplexConsumer : public SemaConsumer { ASTMutationListener *GetASTMutationListener() override; ASTDeserializationListener *GetASTDeserializationListener() override; void PrintStats() override; + bool shouldSkipFunctionBody(Decl *D) override; // SemaConsumer void InitializeSema(Sema &S) override; diff --git a/lib/Frontend/MultiplexConsumer.cpp b/lib/Frontend/MultiplexConsumer.cpp index fdeb04f6248..d1931feeb35 100644 --- a/lib/Frontend/MultiplexConsumer.cpp +++ b/lib/Frontend/MultiplexConsumer.cpp @@ -355,6 +355,13 @@ void MultiplexConsumer::PrintStats() { Consumer->PrintStats(); } +bool MultiplexConsumer::shouldSkipFunctionBody(Decl *D) { + bool Skip = true; + for (auto &Consumer : Consumers) + Skip = Skip && Consumer->shouldSkipFunctionBody(D); + return Skip; +} + void MultiplexConsumer::InitializeSema(Sema &S) { for (auto &Consumer : Consumers) if (SemaConsumer *SC = dyn_cast(Consumer.get())) From bc27f76b3ee7e191b145213a2f51b7b6e566b78c Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Tue, 9 Feb 2016 19:07:19 +0000 Subject: [PATCH 343/742] [libclang] indexing: for a synthesized property reference have the parent be the ObjC implementation decl. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260253 91177308-0d34-0410-b5e6-96231b3b80d8 --- tools/libclang/IndexingContext.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/libclang/IndexingContext.cpp b/tools/libclang/IndexingContext.cpp index 5d944bae1cb..7f65412f989 100644 --- a/tools/libclang/IndexingContext.cpp +++ b/tools/libclang/IndexingContext.cpp @@ -592,8 +592,9 @@ bool IndexingContext::handleObjCMethod(const ObjCMethodDecl *D) { bool IndexingContext::handleSynthesizedObjCProperty( const ObjCPropertyImplDecl *D) { ObjCPropertyDecl *PD = D->getPropertyDecl(); - return handleReference(PD, D->getLocation(), getCursor(D), nullptr, - D->getDeclContext()); + auto *DC = D->getDeclContext(); + return handleReference(PD, D->getLocation(), getCursor(D), + dyn_cast(DC), DC); } bool IndexingContext::handleSynthesizedObjCMethod(const ObjCMethodDecl *D, From 24fa756ec037a8ed1b44bea0dac6a0d6ffd081d1 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Tue, 9 Feb 2016 19:07:21 +0000 Subject: [PATCH 344/742] [libclang] indexing: handle 'TopLevelDeclInObjCContainers' at the point where they are reported. It isn't much benefit and doesn't worth the complexity to try to handle them after the container is encountered. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260254 91177308-0d34-0410-b5e6-96231b3b80d8 --- tools/libclang/IndexDecl.cpp | 16 ---------------- tools/libclang/Indexing.cpp | 5 ++--- tools/libclang/IndexingContext.h | 7 ------- 3 files changed, 2 insertions(+), 26 deletions(-) diff --git a/tools/libclang/IndexDecl.cpp b/tools/libclang/IndexDecl.cpp index c8cf1d36214..aa97129c23f 100644 --- a/tools/libclang/IndexDecl.cpp +++ b/tools/libclang/IndexDecl.cpp @@ -136,7 +136,6 @@ class IndexingDeclVisitor : public ConstDeclVisitor { IndexCtx.handleObjCInterface(D); if (D->isThisDeclarationADefinition()) { - IndexCtx.indexTUDeclsInObjCContainer(); IndexCtx.indexDeclContext(D); } return true; @@ -146,7 +145,6 @@ class IndexingDeclVisitor : public ConstDeclVisitor { IndexCtx.handleObjCProtocol(D); if (D->isThisDeclarationADefinition()) { - IndexCtx.indexTUDeclsInObjCContainer(); IndexCtx.indexDeclContext(D); } return true; @@ -162,8 +160,6 @@ class IndexingDeclVisitor : public ConstDeclVisitor { IndexCtx.handleObjCImplementation(D); - IndexCtx.indexTUDeclsInObjCContainer(); - // Index the ivars first to make sure the synthesized ivars are indexed // before indexing the methods that can reference them. for (const auto *IvarI : D->ivars()) @@ -178,8 +174,6 @@ class IndexingDeclVisitor : public ConstDeclVisitor { bool VisitObjCCategoryDecl(const ObjCCategoryDecl *D) { IndexCtx.handleObjCCategory(D); - - IndexCtx.indexTUDeclsInObjCContainer(); IndexCtx.indexDeclContext(D); return true; } @@ -190,8 +184,6 @@ class IndexingDeclVisitor : public ConstDeclVisitor { return true; IndexCtx.handleObjCCategoryImpl(D); - - IndexCtx.indexTUDeclsInObjCContainer(); IndexCtx.indexDeclContext(D); return true; } @@ -347,11 +339,3 @@ void IndexingContext::indexDeclGroupRef(DeclGroupRef DG) { for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) indexTopLevelDecl(*I); } - -void IndexingContext::indexTUDeclsInObjCContainer() { - while (!TUDeclsInObjCContainer.empty()) { - DeclGroupRef DG = TUDeclsInObjCContainer.front(); - TUDeclsInObjCContainer.pop_front(); - indexDeclGroupRef(DG); - } -} diff --git a/tools/libclang/Indexing.cpp b/tools/libclang/Indexing.cpp index d6e35b0019c..4929d6244e5 100644 --- a/tools/libclang/Indexing.cpp +++ b/tools/libclang/Indexing.cpp @@ -327,9 +327,8 @@ class IndexingConsumer : public ASTConsumer { /// \brief Handle the specified top-level declaration that occurred inside /// and ObjC container. - void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override { - // They will be handled after the interface is seen first. - IndexCtx.addTUDeclInObjCContainer(D); + void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) override { + IndexCtx.indexDeclGroupRef(DG); } /// \brief This is called by the AST reader when deserializing things. diff --git a/tools/libclang/IndexingContext.h b/tools/libclang/IndexingContext.h index 2b1355ad32a..d1d62c90d45 100644 --- a/tools/libclang/IndexingContext.h +++ b/tools/libclang/IndexingContext.h @@ -292,8 +292,6 @@ class IndexingContext { typedef std::pair RefFileOccurrence; llvm::DenseSet RefFileOccurrences; - std::deque TUDeclsInObjCContainer; - llvm::BumpPtrAllocator StrScratch; unsigned StrAdapterCount; friend class ScratchAlloc; @@ -446,13 +444,8 @@ class IndexingContext { bool isNotFromSourceFile(SourceLocation Loc) const; void indexTopLevelDecl(const Decl *D); - void indexTUDeclsInObjCContainer(); void indexDeclGroupRef(DeclGroupRef DG); - void addTUDeclInObjCContainer(DeclGroupRef DG) { - TUDeclsInObjCContainer.push_back(DG); - } - void translateLoc(SourceLocation Loc, CXIdxClientFile *indexFile, CXFile *file, unsigned *line, unsigned *column, unsigned *offset); From a6a8ec48d368d1f8fae225c3df8f6f393828dbd5 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Tue, 9 Feb 2016 19:07:24 +0000 Subject: [PATCH 345/742] [libclang] indexing: make sure to not visit init-list expressions twice. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260255 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/Index/index-refs.cpp | 8 +++++ tools/libclang/IndexBody.cpp | 69 ++++++++++++++++++++++++++++++------ 2 files changed, 66 insertions(+), 11 deletions(-) diff --git a/test/Index/index-refs.cpp b/test/Index/index-refs.cpp index adbf02a7c6e..5a1ba1e8b43 100644 --- a/test/Index/index-refs.cpp +++ b/test/Index/index-refs.cpp @@ -69,6 +69,8 @@ void foo5() { struct S2 s = { .y = 1, .x = 4}; } +int ginitlist[] = {EnumVal}; + // RUN: c-index-test -index-file %s | FileCheck %s // CHECK: [indexDeclaration]: kind: namespace | name: NS // CHECK-NEXT: [indexDeclaration]: kind: variable | name: gx @@ -119,3 +121,9 @@ void foo5() { // CHECK: [indexEntityReference]: kind: field | name: y | {{.*}} | loc: 69:20 // CHECK-NEXT: [indexEntityReference]: kind: field | name: x | {{.*}} | loc: 69:28 +// CHECK-NOT: [indexEntityReference]: kind: field | name: y | {{.*}} | loc: 69:20 +// CHECK-NOT: [indexEntityReference]: kind: field | name: x | {{.*}} | loc: 69:28 + +// CHECK: [indexDeclaration]: kind: variable | name: ginitlist | +// CHECK: [indexEntityReference]: kind: enumerator | name: EnumVal | {{.*}} | loc: 72:20 +// CHECK-NOT: [indexEntityReference]: kind: enumerator | name: EnumVal | {{.*}} | loc: 72:20 diff --git a/tools/libclang/IndexBody.cpp b/tools/libclang/IndexBody.cpp index 64df4b85bea..58dc11722bf 100644 --- a/tools/libclang/IndexBody.cpp +++ b/tools/libclang/IndexBody.cpp @@ -50,17 +50,6 @@ class BodyIndexer : public RecursiveASTVisitor { return true; } - bool VisitDesignatedInitExpr(DesignatedInitExpr *E) { - for (DesignatedInitExpr::reverse_designators_iterator - D = E->designators_rbegin(), DEnd = E->designators_rend(); - D != DEnd; ++D) { - if (D->isFieldDesignator()) - IndexCtx.handleReference(D->getField(), D->getFieldLoc(), - Parent, ParentDC, E); - } - return true; - } - bool VisitObjCIvarRefExpr(ObjCIvarRefExpr *E) { IndexCtx.handleReference(E->getDecl(), E->getLocation(), Parent, ParentDC, E); @@ -162,6 +151,64 @@ class BodyIndexer : public RecursiveASTVisitor { return true; } + // RecursiveASTVisitor visits both syntactic and semantic forms, duplicating + // the things that we visit. Make sure to only visit the semantic form. + // Also visit things that are in the syntactic form but not the semantic one, + // for example the indices in DesignatedInitExprs. + bool TraverseInitListExpr(InitListExpr *S) { + + class SyntacticFormIndexer : + public RecursiveASTVisitor { + IndexingContext &IndexCtx; + const NamedDecl *Parent; + const DeclContext *ParentDC; + + public: + SyntacticFormIndexer(IndexingContext &indexCtx, + const NamedDecl *Parent, const DeclContext *DC) + : IndexCtx(indexCtx), Parent(Parent), ParentDC(DC) { } + + bool shouldWalkTypesOfTypeLocs() const { return false; } + + bool VisitDesignatedInitExpr(DesignatedInitExpr *E) { + for (DesignatedInitExpr::reverse_designators_iterator + D = E->designators_rbegin(), DEnd = E->designators_rend(); + D != DEnd; ++D) { + if (D->isFieldDesignator()) + IndexCtx.handleReference(D->getField(), D->getFieldLoc(), + Parent, ParentDC, E); + } + return true; + } + }; + + auto visitForm = [&](InitListExpr *Form) { + for (Stmt *SubStmt : Form->children()) { + if (!TraverseStmt(SubStmt)) + return false; + } + return true; + }; + + InitListExpr *SemaForm = S->isSemanticForm() ? S : S->getSemanticForm(); + InitListExpr *SyntaxForm = S->isSemanticForm() ? S->getSyntacticForm() : S; + + if (SemaForm) { + // Visit things present in syntactic form but not the semantic form. + if (SyntaxForm) { + SyntacticFormIndexer(IndexCtx, Parent, ParentDC).TraverseStmt(SyntaxForm); + } + return visitForm(SemaForm); + } + + // No semantic, try the syntactic. + if (SyntaxForm) { + return visitForm(SyntaxForm); + } + + return true; + } + }; } // anonymous namespace From de737b5616cdb9e05bd8e1b2af2d937d7175cb44 Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Wed, 20 Jan 2016 15:25:30 +0000 Subject: [PATCH 346/742] Silencing several -Wcast-qual warnings; NFC. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258317 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 5944d95c2c79632face71a22e90d2aab053b4cb4) --- tools/libclang/CIndex.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index 79c7b628607..9963f328ff6 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -3583,7 +3583,8 @@ CXEvalResult clang_Cursor_Evaluate(CXCursor C) { expr = Field->getInClassInitializer(); } if (expr) - return (CXEvalResult)evaluateExpr((Expr *)expr, C); + return const_cast(reinterpret_cast( + evaluateExpr(const_cast(expr), C))); return nullptr; } @@ -3596,7 +3597,8 @@ CXEvalResult clang_Cursor_Evaluate(CXCursor C) { } } if (expr) - return (CXEvalResult)evaluateExpr(expr, C); + return const_cast( + reinterpret_cast(evaluateExpr(expr, C))); } return nullptr; } From 1d57b6433cf3542b7f10da35e34dbd2556c43121 Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Thu, 11 Feb 2016 17:04:42 +0000 Subject: [PATCH 347/742] [Modules] Don't infinite recurse on implicit import of circular modules in preamble Update the Preprocessor's VisibleModuleSet when typo-correction creates an implicit module import so that we won't accidentally write an invalid SourceLocation into the preamble AST. This would later lead to infinite recursion when loading the preamble AST because we use the value in ImportLocs to prevent visiting a module twice. rdar://problem/24440990 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260543 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Basic/Module.cpp | 1 + lib/Serialization/ASTReader.cpp | 4 +++- test/Index/Inputs/module.map | 14 ++++++++++++++ .../Index/Inputs/preamble-with-implicit-import-A.h | 1 + .../Index/Inputs/preamble-with-implicit-import-B.h | 3 +++ .../Index/Inputs/preamble-with-implicit-import-C.h | 2 ++ test/Index/Inputs/preamble-with-implicit-import.h | 4 ++++ test/Index/preamble-with-implicit-import.m | 6 ++++++ 8 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 test/Index/Inputs/preamble-with-implicit-import-A.h create mode 100644 test/Index/Inputs/preamble-with-implicit-import-B.h create mode 100644 test/Index/Inputs/preamble-with-implicit-import-C.h create mode 100644 test/Index/Inputs/preamble-with-implicit-import.h create mode 100644 test/Index/preamble-with-implicit-import.m diff --git a/lib/Basic/Module.cpp b/lib/Basic/Module.cpp index a8656749907..b4f8d252d21 100644 --- a/lib/Basic/Module.cpp +++ b/lib/Basic/Module.cpp @@ -497,6 +497,7 @@ void Module::dump() const { void VisibleModuleSet::setVisible(Module *M, SourceLocation Loc, VisibleCallback Vis, ConflictCallback Cb) { + assert(Loc.isValid() && "setVisible expects a valid import location"); if (isVisible(M)) return; diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index b61265bccb8..efb16748590 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -4052,7 +4052,9 @@ void ASTReader::InitializeContext() { if (Module *Imported = getSubmodule(Import.ID)) { makeModuleVisible(Imported, Module::AllVisible, /*ImportLoc=*/Import.ImportLoc); - PP.makeModuleVisible(Imported, Import.ImportLoc); + if (Import.ImportLoc.isValid()) + PP.makeModuleVisible(Imported, Import.ImportLoc); + // FIXME: should we tell Sema to make the module visible too? } } ImportedModules.clear(); diff --git a/test/Index/Inputs/module.map b/test/Index/Inputs/module.map index 4bfc109a8b1..10712accb1c 100644 --- a/test/Index/Inputs/module.map +++ b/test/Index/Inputs/module.map @@ -6,3 +6,17 @@ module ModuleNeedsVFS { framework module * { } module ModuleUndef { header "module-undef.h" } + +module PreambleWithImplicitImport { + module A { + header "preamble-with-implicit-import-A.h" + } + module B { + header "preamble-with-implicit-import-B.h" + export * + } + module C { + header "preamble-with-implicit-import-C.h" + export * + } +} diff --git a/test/Index/Inputs/preamble-with-implicit-import-A.h b/test/Index/Inputs/preamble-with-implicit-import-A.h new file mode 100644 index 00000000000..c6839015909 --- /dev/null +++ b/test/Index/Inputs/preamble-with-implicit-import-A.h @@ -0,0 +1 @@ +// preamble-with-implicit-import-A diff --git a/test/Index/Inputs/preamble-with-implicit-import-B.h b/test/Index/Inputs/preamble-with-implicit-import-B.h new file mode 100644 index 00000000000..17c138dfb5a --- /dev/null +++ b/test/Index/Inputs/preamble-with-implicit-import-B.h @@ -0,0 +1,3 @@ +#pragma once +#include "preamble-with-implicit-import-C.h" // Circular +typedef struct { char x; } Typo; diff --git a/test/Index/Inputs/preamble-with-implicit-import-C.h b/test/Index/Inputs/preamble-with-implicit-import-C.h new file mode 100644 index 00000000000..a3fc1d4fea0 --- /dev/null +++ b/test/Index/Inputs/preamble-with-implicit-import-C.h @@ -0,0 +1,2 @@ +#pragma once +#include "preamble-with-implicit-import-B.h" // Circular diff --git a/test/Index/Inputs/preamble-with-implicit-import.h b/test/Index/Inputs/preamble-with-implicit-import.h new file mode 100644 index 00000000000..1b429678f21 --- /dev/null +++ b/test/Index/Inputs/preamble-with-implicit-import.h @@ -0,0 +1,4 @@ +#include "preamble-with-implicit-import-A.h" + +// Typo is defined in B, which is not imported. +void useTypeFromB(Typo *); diff --git a/test/Index/preamble-with-implicit-import.m b/test/Index/preamble-with-implicit-import.m new file mode 100644 index 00000000000..e3d0e8b1a62 --- /dev/null +++ b/test/Index/preamble-with-implicit-import.m @@ -0,0 +1,6 @@ +// RUN: rm -rf %t +// RUN: env CINDEXTEST_EDITING=1 c-index-test -test-load-source-reparse 2 none %s -I %S/Inputs -fmodules -fmodules-cache-path=%t -fspell-checking 2>&1 | FileCheck %s +// CHECK: error: declaration of 'Typo' must be imported +// CHECK: error: declaration of 'Typo' must be imported + +#include "preamble-with-implicit-import.h" From 213818e875b4780614bb9ba7265782acbeb92052 Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Thu, 11 Feb 2016 18:54:02 +0000 Subject: [PATCH 348/742] [Modules] Early-exit if ReadOptionsBlock fails to avoid crashing If we didn't tell ReadOptionsBlock to allow failures then we can't assume that the stream is not in the middle of a block if it returns out-of-date. This was causing a crash when we tried to continue reading. Also, it's just generally a good idea to early-exit if we're doing implicit module builds, since we will want to immediately rebuild this module anyway and there's no reason to waste time continuing after failure. rdar://problem/24114938 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260563 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Serialization/ASTReader.cpp | 7 ++++--- test/Modules/implicit-build-config-out-of-date.m | 6 ++++++ 2 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 test/Modules/implicit-build-config-out-of-date.m diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index efb16748590..b12c6b420be 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -2254,9 +2254,10 @@ ASTReader::ReadControlBlock(ModuleFile &F, (AllowConfigurationMismatch && Result == ConfigurationMismatch)) Result = Success; - // If we've diagnosed a problem, we're done. - if (Result != Success && - isDiagnosedResult(Result, ClientLoadCapabilities)) + // If we can't load the module, exit early since we likely + // will rebuild the module anyway. The stream may be in the + // middle of a block. + if (Result != Success) return Result; } else if (Stream.SkipBlock()) { Error("malformed block record in AST file"); diff --git a/test/Modules/implicit-build-config-out-of-date.m b/test/Modules/implicit-build-config-out-of-date.m new file mode 100644 index 00000000000..c8c02ff0a80 --- /dev/null +++ b/test/Modules/implicit-build-config-out-of-date.m @@ -0,0 +1,6 @@ +// RUN: rm -rf %t +// Use -DA=0 so that there is at least one preprocessor option serialized after the diagnostic options. +// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -fimplicit-module-maps -I %S/Inputs %s -DA=0 -Rmodule-build -verify +// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -fimplicit-module-maps -I %S/Inputs %s -DA=0 -Werror -Rmodule-build -verify + +@import category_top; // expected-remark {{building module}} expected-remark {{finished building}} From b27ed6857cd20b8fb32a0cad4ecfe82ad25d24f7 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Sat, 30 Jan 2016 01:51:20 +0000 Subject: [PATCH 349/742] [SemaCXX] Fix crash-on-invalid while trying to deduce return type of a lambda. rdar://22032373 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259287 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/SemaStmt.cpp | 9 +++++---- test/SemaCXX/lambda-expressions.cpp | 11 +++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index e1b1a47e182..d73441646bb 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -3066,22 +3066,23 @@ bool Sema::DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD, // has multiple return statements, the return type is deduced for each return // statement. [...] if the type deduced is not the same in each deduction, // the program is ill-formed. - if (AT->isDeduced() && !FD->isInvalidDecl()) { + QualType DeducedT = AT->getDeducedType(); + if (!DeducedT.isNull() && !FD->isInvalidDecl()) { AutoType *NewAT = Deduced->getContainedAutoType(); CanQualType OldDeducedType = Context.getCanonicalFunctionResultType( - AT->getDeducedType()); + DeducedT); CanQualType NewDeducedType = Context.getCanonicalFunctionResultType( NewAT->getDeducedType()); if (!FD->isDependentContext() && OldDeducedType != NewDeducedType) { const LambdaScopeInfo *LambdaSI = getCurLambda(); if (LambdaSI && LambdaSI->HasImplicitReturnType) { Diag(ReturnLoc, diag::err_typecheck_missing_return_type_incompatible) - << NewAT->getDeducedType() << AT->getDeducedType() + << NewAT->getDeducedType() << DeducedT << true /*IsLambda*/; } else { Diag(ReturnLoc, diag::err_auto_fn_different_deductions) << (AT->isDecltypeAuto() ? 1 : 0) - << NewAT->getDeducedType() << AT->getDeducedType(); + << NewAT->getDeducedType() << DeducedT; } return true; } diff --git a/test/SemaCXX/lambda-expressions.cpp b/test/SemaCXX/lambda-expressions.cpp index 72adcdbce2f..08446a0ee43 100644 --- a/test/SemaCXX/lambda-expressions.cpp +++ b/test/SemaCXX/lambda-expressions.cpp @@ -476,3 +476,14 @@ int main() { A a; } + +// rdar://22032373 +namespace rdar22032373 { +void foo() { + auto blk = [](bool b) { + if (b) + return undeclared_error; // expected-error {{use of undeclared identifier}} + return 0; + }; +} +} From b83007ccf81b85eea2e72efb8b54221f26bffd5e Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Fri, 12 Feb 2016 23:10:59 +0000 Subject: [PATCH 350/742] [libclang] Separate the underlying indexing functionality of libclang and introduce it into the clangIndex library. It is a general goodness for libclang itself to mostly be a wrapper of functionality provided by the libraries. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260760 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Index/IndexDataConsumer.h | 61 ++ include/clang/Index/IndexSymbol.h | 112 ++++ include/clang/Index/IndexingAction.h | 47 ++ lib/Index/CMakeLists.txt | 8 + lib/Index/IndexBody.cpp | 347 +++++++++++ {tools/libclang => lib/Index}/IndexDecl.cpp | 272 ++++++--- lib/Index/IndexSymbol.cpp | 187 ++++++ .../Index}/IndexTypeSourceInfo.cpp | 98 ++- lib/Index/IndexingAction.cpp | 140 +++++ lib/Index/IndexingContext.cpp | 325 ++++++++++ lib/Index/IndexingContext.h | 121 ++++ tools/libclang/CMakeLists.txt | 5 +- ...ingContext.cpp => CXIndexDataConsumer.cpp} | 557 +++++++++++------- ...ndexingContext.h => CXIndexDataConsumer.h} | 45 +- tools/libclang/IndexBody.cpp | 224 ------- tools/libclang/Indexing.cpp | 168 ++---- 16 files changed, 2023 insertions(+), 694 deletions(-) create mode 100644 include/clang/Index/IndexDataConsumer.h create mode 100644 include/clang/Index/IndexSymbol.h create mode 100644 include/clang/Index/IndexingAction.h create mode 100644 lib/Index/IndexBody.cpp rename {tools/libclang => lib/Index}/IndexDecl.cpp (50%) create mode 100644 lib/Index/IndexSymbol.cpp rename {tools/libclang => lib/Index}/IndexTypeSourceInfo.cpp (52%) create mode 100644 lib/Index/IndexingAction.cpp create mode 100644 lib/Index/IndexingContext.cpp create mode 100644 lib/Index/IndexingContext.h rename tools/libclang/{IndexingContext.cpp => CXIndexDataConsumer.cpp} (70%) rename tools/libclang/{IndexingContext.h => CXIndexDataConsumer.h} (91%) delete mode 100644 tools/libclang/IndexBody.cpp diff --git a/include/clang/Index/IndexDataConsumer.h b/include/clang/Index/IndexDataConsumer.h new file mode 100644 index 00000000000..cb00345f56b --- /dev/null +++ b/include/clang/Index/IndexDataConsumer.h @@ -0,0 +1,61 @@ +//===--- IndexDataConsumer.h - Abstract index data consumer ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXDATACONSUMER_H +#define LLVM_CLANG_INDEX_INDEXDATACONSUMER_H + +#include "clang/Index/IndexSymbol.h" + +namespace clang { + class DeclContext; + class Expr; + class FileID; + class IdentifierInfo; + class ImportDecl; + class MacroInfo; + +namespace index { + +class IndexDataConsumer { +public: + struct ASTNodeInfo { + const Expr *OrigE; + const Decl *OrigD; + const Decl *Parent; + const DeclContext *ContainerDC; + }; + + virtual ~IndexDataConsumer() {} + + /// \returns true to continue indexing, or false to abort. + virtual bool handleDeclOccurence(const Decl *D, SymbolRoleSet Roles, + ArrayRef Relations, + FileID FID, unsigned Offset, + ASTNodeInfo ASTNode); + + /// \returns true to continue indexing, or false to abort. + virtual bool handleMacroOccurence(const IdentifierInfo *Name, + const MacroInfo *MI, SymbolRoleSet Roles, + FileID FID, unsigned Offset); + + /// \returns true to continue indexing, or false to abort. + virtual bool handleModuleOccurence(const ImportDecl *ImportD, + SymbolRoleSet Roles, + FileID FID, unsigned Offset); + + virtual void finish() {} + +private: + virtual void _anchor(); +}; + +} // namespace index +} // namespace clang + +#endif diff --git a/include/clang/Index/IndexSymbol.h b/include/clang/Index/IndexSymbol.h new file mode 100644 index 00000000000..0849241b60e --- /dev/null +++ b/include/clang/Index/IndexSymbol.h @@ -0,0 +1,112 @@ +//===--- IndexSymbol.h - Types and functions for indexing symbols ---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXSYMBOL_H +#define LLVM_CLANG_INDEX_INDEXSYMBOL_H + +#include "clang/Basic/LLVM.h" + +namespace clang { + class Decl; + +namespace index { + +enum class SymbolKind : uint8_t { + Unknown, + + Module, + Macro, + + Enum, + Struct, + Union, + Typedef, + + Function, + Variable, + Field, + EnumConstant, + + ObjCClass, + ObjCProtocol, + ObjCCategory, + + ObjCInstanceMethod, + ObjCClassMethod, + ObjCProperty, + ObjCIvar, + + CXXClass, + CXXNamespace, + CXXNamespaceAlias, + CXXStaticVariable, + CXXStaticMethod, + CXXInstanceMethod, + CXXConstructor, + CXXDestructor, + CXXConversionFunction, + CXXTypeAlias, + CXXInterface, +}; + +enum class SymbolLanguage { + C, + ObjC, + CXX, +}; + +enum class SymbolCXXTemplateKind { + NonTemplate, + Template, + TemplatePartialSpecialization, + TemplateSpecialization, +}; + +/// Set of roles that are attributed to symbol occurrences. +enum class SymbolRole : uint16_t { + Declaration = 1 << 0, + Definition = 1 << 1, + Reference = 1 << 2, + Read = 1 << 3, + Write = 1 << 4, + Call = 1 << 5, + Dynamic = 1 << 6, + AddressOf = 1 << 7, + Implicit = 1 << 8, + + // Relation roles. + RelationChildOf = 1 << 9, + RelationBaseOf = 1 << 10, + RelationOverrideOf = 1 << 11, + RelationReceivedBy = 1 << 12, +}; +static const unsigned SymbolRoleBitNum = 13; +typedef unsigned SymbolRoleSet; + +/// Represents a relation to another symbol for a symbol occurrence. +struct SymbolRelation { + SymbolRoleSet Roles; + const Decl *RelatedSymbol; + + SymbolRelation(SymbolRoleSet Roles, const Decl *Sym) + : Roles(Roles), RelatedSymbol(Sym) {} +}; + +struct SymbolInfo { + SymbolKind Kind; + SymbolCXXTemplateKind TemplateKind; + SymbolLanguage Lang; +}; + +SymbolInfo getSymbolInfo(const Decl *D); + +} // namespace index +} // namespace clang + +#endif diff --git a/include/clang/Index/IndexingAction.h b/include/clang/Index/IndexingAction.h new file mode 100644 index 00000000000..dfc363a049d --- /dev/null +++ b/include/clang/Index/IndexingAction.h @@ -0,0 +1,47 @@ +//===--- IndexingAction.h - Frontend index action -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_INDEXINGACTION_H +#define LLVM_CLANG_INDEX_INDEXINGACTION_H + +#include "clang/Basic/LLVM.h" +#include + +namespace clang { + class ASTUnit; + class FrontendAction; + +namespace index { + class IndexDataConsumer; + +struct IndexingOptions { + enum class SystemSymbolFilterKind { + None, + DeclarationsOnly, + All, + }; + + SystemSymbolFilterKind SystemSymbolFilter + = SystemSymbolFilterKind::DeclarationsOnly; + bool IndexFunctionLocals = false; +}; + +std::unique_ptr +createIndexingAction(std::unique_ptr WrappedAction, + std::shared_ptr DataConsumer, + IndexingOptions Opts); + +void indexASTUnit(ASTUnit &Unit, + std::shared_ptr DataConsumer, + IndexingOptions Opts); + +} // namespace index +} // namespace clang + +#endif diff --git a/lib/Index/CMakeLists.txt b/lib/Index/CMakeLists.txt index 3869c32c879..cb2b931235e 100644 --- a/lib/Index/CMakeLists.txt +++ b/lib/Index/CMakeLists.txt @@ -5,14 +5,22 @@ set(LLVM_LINK_COMPONENTS add_clang_library(clangIndex CommentToXML.cpp USRGeneration.cpp + IndexBody.cpp + IndexDecl.cpp + IndexingAction.cpp + IndexingContext.cpp + IndexSymbol.cpp + IndexTypeSourceInfo.cpp ADDITIONAL_HEADERS + IndexingContext.h SimpleFormatContext.h LINK_LIBS clangAST clangBasic clangFormat + clangFrontend clangRewrite clangToolingCore ) diff --git a/lib/Index/IndexBody.cpp b/lib/Index/IndexBody.cpp new file mode 100644 index 00000000000..56fba28387d --- /dev/null +++ b/lib/Index/IndexBody.cpp @@ -0,0 +1,347 @@ +//===- IndexBody.cpp - Indexing statements --------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "IndexingContext.h" +#include "clang/AST/RecursiveASTVisitor.h" + +using namespace clang; +using namespace clang::index; + +namespace { + +class BodyIndexer : public RecursiveASTVisitor { + IndexingContext &IndexCtx; + const NamedDecl *Parent; + const DeclContext *ParentDC; + SmallVector StmtStack; + + typedef RecursiveASTVisitor base; +public: + BodyIndexer(IndexingContext &indexCtx, + const NamedDecl *Parent, const DeclContext *DC) + : IndexCtx(indexCtx), Parent(Parent), ParentDC(DC) { } + + bool shouldWalkTypesOfTypeLocs() const { return false; } + + bool TraverseStmt(Stmt *S) { + StmtStack.push_back(S); + bool ret = base::TraverseStmt(S); + StmtStack.pop_back(); + return ret; + } + + bool TraverseTypeLoc(TypeLoc TL) { + IndexCtx.indexTypeLoc(TL, Parent, ParentDC); + return true; + } + + bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) { + IndexCtx.indexNestedNameSpecifierLoc(NNS, Parent, ParentDC); + return true; + } + + SymbolRoleSet getRolesForRef(const Expr *E, + SmallVectorImpl &Relations) { + SymbolRoleSet Roles{}; + assert(!StmtStack.empty() && E == StmtStack.back()); + if (StmtStack.size() == 1) + return Roles; + auto It = StmtStack.end()-2; + while (isa(*It) || isa(*It)) { + if (auto ICE = dyn_cast(*It)) { + if (ICE->getCastKind() == CK_LValueToRValue) + Roles |= (unsigned)(unsigned)SymbolRole::Read; + } + if (It == StmtStack.begin()) + break; + --It; + } + const Stmt *Parent = *It; + + if (auto BO = dyn_cast(Parent)) { + if (BO->getOpcode() == BO_Assign && BO->getLHS()->IgnoreParenCasts() == E) + Roles |= (unsigned)SymbolRole::Write; + + } else if (auto UO = dyn_cast(Parent)) { + if (UO->isIncrementDecrementOp()) { + Roles |= (unsigned)SymbolRole::Read; + Roles |= (unsigned)SymbolRole::Write; + } else if (UO->getOpcode() == UO_AddrOf) { + Roles |= (unsigned)SymbolRole::AddressOf; + } + + } else if (auto CA = dyn_cast(Parent)) { + if (CA->getLHS()->IgnoreParenCasts() == E) { + Roles |= (unsigned)SymbolRole::Read; + Roles |= (unsigned)SymbolRole::Write; + } + + } else if (auto CE = dyn_cast(Parent)) { + if (CE->getCallee()->IgnoreParenCasts() == E) { + Roles |= (unsigned)SymbolRole::Call; + if (auto *ME = dyn_cast(E)) { + if (auto *CXXMD = dyn_cast_or_null(ME->getMemberDecl())) + if (CXXMD->isVirtual() && !ME->hasQualifier()) { + Roles |= (unsigned)SymbolRole::Dynamic; + auto BaseTy = ME->getBase()->IgnoreImpCasts()->getType(); + if (!BaseTy.isNull()) + if (auto *CXXRD = BaseTy->getPointeeCXXRecordDecl()) + Relations.emplace_back((unsigned)SymbolRole::RelationReceivedBy, + CXXRD); + } + } + } else if (auto CXXOp = dyn_cast(CE)) { + if (CXXOp->getNumArgs() > 0 && CXXOp->getArg(0)->IgnoreParenCasts() == E) { + OverloadedOperatorKind Op = CXXOp->getOperator(); + if (Op == OO_Equal) { + Roles |= (unsigned)SymbolRole::Write; + } else if ((Op >= OO_PlusEqual && Op <= OO_PipeEqual) || + Op == OO_LessLessEqual || Op == OO_GreaterGreaterEqual || + Op == OO_PlusPlus || Op == OO_MinusMinus) { + Roles |= (unsigned)SymbolRole::Read; + Roles |= (unsigned)SymbolRole::Write; + } else if (Op == OO_Amp) { + Roles |= (unsigned)SymbolRole::AddressOf; + } + } + } + } + + return Roles; + } + + bool VisitDeclRefExpr(DeclRefExpr *E) { + SmallVector Relations; + SymbolRoleSet Roles = getRolesForRef(E, Relations); + return IndexCtx.handleReference(E->getDecl(), E->getLocation(), + Parent, ParentDC, Roles, Relations, E); + } + + bool VisitMemberExpr(MemberExpr *E) { + SourceLocation Loc = E->getMemberLoc(); + if (Loc.isInvalid()) + Loc = E->getLocStart(); + SmallVector Relations; + SymbolRoleSet Roles = getRolesForRef(E, Relations); + return IndexCtx.handleReference(E->getMemberDecl(), Loc, + Parent, ParentDC, Roles, Relations, E); + } + + bool VisitDesignatedInitExpr(DesignatedInitExpr *E) { + for (DesignatedInitExpr::reverse_designators_iterator + D = E->designators_rbegin(), DEnd = E->designators_rend(); + D != DEnd; ++D) { + if (D->isFieldDesignator()) + return IndexCtx.handleReference(D->getField(), D->getFieldLoc(), + Parent, ParentDC, SymbolRoleSet(), + {}, E); + } + return true; + } + + bool VisitObjCIvarRefExpr(ObjCIvarRefExpr *E) { + SmallVector Relations; + SymbolRoleSet Roles = getRolesForRef(E, Relations); + return IndexCtx.handleReference(E->getDecl(), E->getLocation(), + Parent, ParentDC, Roles, Relations, E); + } + + bool VisitObjCMessageExpr(ObjCMessageExpr *E) { + auto isDynamic = [](const ObjCMessageExpr *MsgE)->bool { + if (MsgE->getReceiverKind() != ObjCMessageExpr::Instance) + return false; + if (auto *RecE = dyn_cast( + MsgE->getInstanceReceiver()->IgnoreParenCasts())) { + if (RecE->getMethodFamily() == OMF_alloc) + return false; + } + return true; + }; + + if (ObjCMethodDecl *MD = E->getMethodDecl()) { + SymbolRoleSet Roles = (unsigned)SymbolRole::Call; + if (E->isImplicit()) + Roles |= (unsigned)SymbolRole::Implicit; + + SmallVector Relations; + if (isDynamic(E)) { + Roles |= (unsigned)SymbolRole::Dynamic; + if (auto *RecD = E->getReceiverInterface()) + Relations.emplace_back((unsigned)SymbolRole::RelationReceivedBy, RecD); + } + + return IndexCtx.handleReference(MD, E->getSelectorStartLoc(), + Parent, ParentDC, Roles, Relations, E); + } + return true; + } + + bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *E) { + if (E->isExplicitProperty()) + return IndexCtx.handleReference(E->getExplicitProperty(), E->getLocation(), + Parent, ParentDC, SymbolRoleSet(), {}, E); + + // No need to do a handleReference for the objc method, because there will + // be a message expr as part of PseudoObjectExpr. + return true; + } + + bool VisitMSPropertyRefExpr(MSPropertyRefExpr *E) { + return IndexCtx.handleReference(E->getPropertyDecl(), E->getMemberLoc(), + Parent, ParentDC, SymbolRoleSet(), {}, E); + } + + bool VisitObjCProtocolExpr(ObjCProtocolExpr *E) { + return IndexCtx.handleReference(E->getProtocol(), E->getProtocolIdLoc(), + Parent, ParentDC, SymbolRoleSet(), {}, E); + } + + bool VisitObjCBoxedExpr(ObjCBoxedExpr *E) { + if (ObjCMethodDecl *MD = E->getBoxingMethod()) { + SymbolRoleSet Roles = (unsigned)SymbolRole::Call; + Roles |= (unsigned)SymbolRole::Implicit; + return IndexCtx.handleReference(MD, E->getLocStart(), + Parent, ParentDC, Roles, {}, E); + } + return true; + } + + bool VisitObjCDictionaryLiteral(ObjCDictionaryLiteral *E) { + if (ObjCMethodDecl *MD = E->getDictWithObjectsMethod()) { + SymbolRoleSet Roles = (unsigned)SymbolRole::Call; + Roles |= (unsigned)SymbolRole::Implicit; + return IndexCtx.handleReference(MD, E->getLocStart(), + Parent, ParentDC, Roles, {}, E); + } + return true; + } + + bool VisitObjCArrayLiteral(ObjCArrayLiteral *E) { + if (ObjCMethodDecl *MD = E->getArrayWithObjectsMethod()) { + SymbolRoleSet Roles = (unsigned)SymbolRole::Call; + Roles |= (unsigned)SymbolRole::Implicit; + return IndexCtx.handleReference(MD, E->getLocStart(), + Parent, ParentDC, Roles, {}, E); + } + return true; + } + + bool VisitCXXConstructExpr(CXXConstructExpr *E) { + return IndexCtx.handleReference(E->getConstructor(), E->getLocation(), + Parent, ParentDC, (unsigned)SymbolRole::Call, {}, E); + } + + bool TraverseCXXOperatorCallExpr(CXXOperatorCallExpr *E, + DataRecursionQueue *Q = nullptr) { + if (E->getOperatorLoc().isInvalid()) + return true; // implicit. + return base::TraverseCXXOperatorCallExpr(E); + } + + bool VisitDeclStmt(DeclStmt *S) { + if (IndexCtx.shouldIndexFunctionLocalSymbols()) { + IndexCtx.indexDeclGroupRef(S->getDeclGroup()); + return true; + } + + DeclGroupRef DG = S->getDeclGroup(); + for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) { + const Decl *D = *I; + if (!D) + continue; + if (!IndexCtx.isFunctionLocalDecl(D)) + IndexCtx.indexTopLevelDecl(D); + } + + return true; + } + + bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C) { + if (C->capturesThis() || C->capturesVLAType()) + return true; + + if (C->capturesVariable() && IndexCtx.shouldIndexFunctionLocalSymbols()) + return IndexCtx.handleReference(C->getCapturedVar(), C->getLocation(), + Parent, ParentDC, SymbolRoleSet()); + + // FIXME: Lambda init-captures. + return true; + } + + // RecursiveASTVisitor visits both syntactic and semantic forms, duplicating + // the things that we visit. Make sure to only visit the semantic form. + // Also visit things that are in the syntactic form but not the semantic one, + // for example the indices in DesignatedInitExprs. + bool TraverseInitListExpr(InitListExpr *S, DataRecursionQueue *Q = nullptr) { + + class SyntacticFormIndexer : + public RecursiveASTVisitor { + IndexingContext &IndexCtx; + const NamedDecl *Parent; + const DeclContext *ParentDC; + + public: + SyntacticFormIndexer(IndexingContext &indexCtx, + const NamedDecl *Parent, const DeclContext *DC) + : IndexCtx(indexCtx), Parent(Parent), ParentDC(DC) { } + + bool shouldWalkTypesOfTypeLocs() const { return false; } + + bool VisitDesignatedInitExpr(DesignatedInitExpr *E) { + for (DesignatedInitExpr::reverse_designators_iterator + D = E->designators_rbegin(), DEnd = E->designators_rend(); + D != DEnd; ++D) { + if (D->isFieldDesignator()) + return IndexCtx.handleReference(D->getField(), D->getFieldLoc(), + Parent, ParentDC, SymbolRoleSet(), + {}, E); + } + return true; + } + }; + + auto visitForm = [&](InitListExpr *Form) { + for (Stmt *SubStmt : Form->children()) { + if (!TraverseStmt(SubStmt)) + return false; + } + return true; + }; + + InitListExpr *SemaForm = S->isSemanticForm() ? S : S->getSemanticForm(); + InitListExpr *SyntaxForm = S->isSemanticForm() ? S->getSyntacticForm() : S; + + if (SemaForm) { + // Visit things present in syntactic form but not the semantic form. + if (SyntaxForm) { + SyntacticFormIndexer(IndexCtx, Parent, ParentDC).TraverseStmt(SyntaxForm); + } + return visitForm(SemaForm); + } + + // No semantic, try the syntactic. + if (SyntaxForm) { + return visitForm(SyntaxForm); + } + + return true; + } +}; + +} // anonymous namespace + +void IndexingContext::indexBody(const Stmt *S, const NamedDecl *Parent, + const DeclContext *DC) { + if (!S) + return; + + if (!DC) + DC = Parent->getLexicalDeclContext(); + BodyIndexer(*this, Parent, DC).TraverseStmt(const_cast(S)); +} diff --git a/tools/libclang/IndexDecl.cpp b/lib/Index/IndexDecl.cpp similarity index 50% rename from tools/libclang/IndexDecl.cpp rename to lib/Index/IndexDecl.cpp index aa97129c23f..76f68e564c9 100644 --- a/tools/libclang/IndexDecl.cpp +++ b/lib/Index/IndexDecl.cpp @@ -1,4 +1,4 @@ -//===- CIndexHigh.cpp - Higher level API functions ------------------------===// +//===- IndexDecl.cpp - Indexing declarations ------------------------------===// // // The LLVM Compiler Infrastructure // @@ -8,10 +8,11 @@ //===----------------------------------------------------------------------===// #include "IndexingContext.h" +#include "clang/Index/IndexDataConsumer.h" #include "clang/AST/DeclVisitor.h" using namespace clang; -using namespace cxindex; +using namespace index; namespace { @@ -22,6 +23,13 @@ class IndexingDeclVisitor : public ConstDeclVisitor { explicit IndexingDeclVisitor(IndexingContext &indexCtx) : IndexCtx(indexCtx) { } + bool Handled = true; + + bool VisitDecl(const Decl *D) { + Handled = false; + return true; + } + /// \brief Returns true if the given method has been defined explicitly by the /// user. static bool hasUserDefined(const ObjCMethodDecl *D, @@ -35,25 +43,35 @@ class IndexingDeclVisitor : public ConstDeclVisitor { const NamedDecl *Parent = nullptr) { if (!Parent) Parent = D; - if (!IndexCtx.shouldIndexFunctionLocalSymbols()) { - IndexCtx.indexTypeSourceInfo(D->getTypeSourceInfo(), Parent); - IndexCtx.indexNestedNameSpecifierLoc(D->getQualifierLoc(), Parent); - } else { + IndexCtx.indexTypeSourceInfo(D->getTypeSourceInfo(), Parent); + IndexCtx.indexNestedNameSpecifierLoc(D->getQualifierLoc(), Parent); + if (IndexCtx.shouldIndexFunctionLocalSymbols()) { + // Only index parameters in definitions, parameters in declarations are + // not useful. if (const ParmVarDecl *Parm = dyn_cast(D)) { - IndexCtx.handleVar(Parm); + auto *DC = Parm->getDeclContext(); + if (auto *FD = dyn_cast(DC)) { + if (FD->isThisDeclarationADefinition()) + IndexCtx.handleDecl(Parm); + } else if (auto *MD = dyn_cast(DC)) { + if (MD->isThisDeclarationADefinition()) + IndexCtx.handleDecl(Parm); + } else { + IndexCtx.handleDecl(Parm); + } } else if (const FunctionDecl *FD = dyn_cast(D)) { - for (auto PI : FD->params()) { - IndexCtx.handleVar(PI); + if (FD->isThisDeclarationADefinition()) { + for (auto PI : FD->params()) { + IndexCtx.handleDecl(PI); + } } } } } - void handleObjCMethod(const ObjCMethodDecl *D) { - IndexCtx.handleObjCMethod(D); - if (D->isImplicit()) - return; - + bool handleObjCMethod(const ObjCMethodDecl *D) { + if (!IndexCtx.handleDecl(D, (unsigned)SymbolRole::Dynamic)) + return false; IndexCtx.indexTypeSourceInfo(D->getReturnTypeSourceInfo(), D); for (const auto *I : D->params()) handleDeclarator(I, D); @@ -64,10 +82,26 @@ class IndexingDeclVisitor : public ConstDeclVisitor { IndexCtx.indexBody(Body, D, D); } } + return true; } bool VisitFunctionDecl(const FunctionDecl *D) { - IndexCtx.handleFunction(D); + if (D->isDeleted()) + return true; + + SymbolRoleSet Roles{}; + SmallVector Relations; + if (auto *CXXMD = dyn_cast(D)) { + if (CXXMD->isVirtual()) + Roles |= (unsigned)SymbolRole::Dynamic; + for (auto I = CXXMD->begin_overridden_methods(), + E = CXXMD->end_overridden_methods(); I != E; ++I) { + Relations.emplace_back((unsigned)SymbolRole::RelationOverrideOf, *I); + } + } + + if (!IndexCtx.handleDecl(D, Roles, Relations)) + return false; handleDeclarator(D); if (const CXXConstructorDecl *Ctor = dyn_cast(D)) { @@ -76,7 +110,8 @@ class IndexingDeclVisitor : public ConstDeclVisitor { if (Init->isWritten()) { IndexCtx.indexTypeSourceInfo(Init->getTypeSourceInfo(), D); if (const FieldDecl *Member = Init->getAnyMember()) - IndexCtx.handleReference(Member, Init->getMemberLocation(), D, D); + IndexCtx.handleReference(Member, Init->getMemberLocation(), D, D, + (unsigned)SymbolRole::Write); IndexCtx.indexBody(Init->getInit(), D, D); } } @@ -92,14 +127,16 @@ class IndexingDeclVisitor : public ConstDeclVisitor { } bool VisitVarDecl(const VarDecl *D) { - IndexCtx.handleVar(D); + if (!IndexCtx.handleDecl(D)) + return false; handleDeclarator(D); IndexCtx.indexBody(D->getInit(), D); return true; } bool VisitFieldDecl(const FieldDecl *D) { - IndexCtx.handleField(D); + if (!IndexCtx.handleDecl(D)) + return false; handleDeclarator(D); if (D->isBitField()) IndexCtx.indexBody(D->getBitWidth(), D); @@ -108,44 +145,77 @@ class IndexingDeclVisitor : public ConstDeclVisitor { return true; } + bool VisitObjCIvarDecl(const ObjCIvarDecl *D) { + if (D->getSynthesize()) { + // For synthesized ivars, use the location of the ObjC implementation, + // not the location of the property. + // Otherwise the header file containing the @interface will have different + // indexing contents based on whether the @implementation was present or + // not in the translation unit. + return IndexCtx.handleDecl(D, + cast(D->getDeclContext())->getLocation(), + (unsigned)SymbolRole::Implicit); + } + if (!IndexCtx.handleDecl(D)) + return false; + handleDeclarator(D); + return true; + } + bool VisitMSPropertyDecl(const MSPropertyDecl *D) { handleDeclarator(D); return true; } bool VisitEnumConstantDecl(const EnumConstantDecl *D) { - IndexCtx.handleEnumerator(D); + if (!IndexCtx.handleDecl(D)) + return false; IndexCtx.indexBody(D->getInitExpr(), D); return true; } bool VisitTypedefNameDecl(const TypedefNameDecl *D) { - IndexCtx.handleTypedefName(D); + if (!IndexCtx.handleDecl(D)) + return false; IndexCtx.indexTypeSourceInfo(D->getTypeSourceInfo(), D); return true; } bool VisitTagDecl(const TagDecl *D) { // Non-free standing tags are handled in indexTypeSourceInfo. - if (D->isFreeStanding()) - IndexCtx.indexTagDecl(D); + if (D->isFreeStanding()) { + if (D->isThisDeclarationADefinition()) { + IndexCtx.indexTagDecl(D); + } else { + auto *Parent = dyn_cast(D->getDeclContext()); + return IndexCtx.handleReference(D, D->getLocation(), Parent, + D->getLexicalDeclContext(), + SymbolRoleSet()); + } + } return true; } bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *D) { - IndexCtx.handleObjCInterface(D); - if (D->isThisDeclarationADefinition()) { + if (!IndexCtx.handleDecl(D)) + return false; IndexCtx.indexDeclContext(D); + } else { + return IndexCtx.handleReference(D, D->getLocation(), nullptr, nullptr, + SymbolRoleSet()); } return true; } bool VisitObjCProtocolDecl(const ObjCProtocolDecl *D) { - IndexCtx.handleObjCProtocol(D); - if (D->isThisDeclarationADefinition()) { + if (!IndexCtx.handleDecl(D)) + return false; IndexCtx.indexDeclContext(D); + } else { + return IndexCtx.handleReference(D, D->getLocation(), nullptr, nullptr, + SymbolRoleSet()); } return true; } @@ -156,9 +226,10 @@ class IndexingDeclVisitor : public ConstDeclVisitor { return true; if (Class->isImplicitInterfaceDecl()) - IndexCtx.handleObjCInterface(Class); + IndexCtx.handleDecl(Class); - IndexCtx.handleObjCImplementation(D); + if (!IndexCtx.handleDecl(D)) + return false; // Index the ivars first to make sure the synthesized ivars are indexed // before indexing the methods that can reference them. @@ -173,7 +244,8 @@ class IndexingDeclVisitor : public ConstDeclVisitor { } bool VisitObjCCategoryDecl(const ObjCCategoryDecl *D) { - IndexCtx.handleObjCCategory(D); + if (!IndexCtx.handleDecl(D)) + return false; IndexCtx.indexDeclContext(D); return true; } @@ -183,7 +255,8 @@ class IndexingDeclVisitor : public ConstDeclVisitor { if (!Cat) return true; - IndexCtx.handleObjCCategoryImpl(D); + if (!IndexCtx.handleDecl(D)) + return false; IndexCtx.indexDeclContext(D); return true; } @@ -205,14 +278,19 @@ class IndexingDeclVisitor : public ConstDeclVisitor { if (ObjCMethodDecl *MD = D->getSetterMethodDecl()) if (MD->getLexicalDeclContext() == D->getLexicalDeclContext()) handleObjCMethod(MD); - IndexCtx.handleObjCProperty(D); + if (!IndexCtx.handleDecl(D)) + return false; IndexCtx.indexTypeSourceInfo(D->getTypeSourceInfo(), D); return true; } bool VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *D) { ObjCPropertyDecl *PD = D->getPropertyDecl(); - IndexCtx.handleSynthesizedObjCProperty(D); + if (!IndexCtx.handleReference(PD, D->getLocation(), + /*Parent=*/cast(D->getDeclContext()), + D->getDeclContext(), SymbolRoleSet(), {}, + /*RefE=*/nullptr, D)) + return false; if (D->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) return true; @@ -221,121 +299,131 @@ class IndexingDeclVisitor : public ConstDeclVisitor { if (ObjCIvarDecl *IvarD = D->getPropertyIvarDecl()) { if (!IvarD->getSynthesize()) IndexCtx.handleReference(IvarD, D->getPropertyIvarDeclLoc(), nullptr, - D->getDeclContext()); + D->getDeclContext(), SymbolRoleSet()); } + auto *ImplD = cast(D->getDeclContext()); if (ObjCMethodDecl *MD = PD->getGetterMethodDecl()) { if (MD->isPropertyAccessor() && - !hasUserDefined(MD, cast(D->getDeclContext()))) - IndexCtx.handleSynthesizedObjCMethod(MD, D->getLocation(), - D->getLexicalDeclContext()); + !hasUserDefined(MD, ImplD)) + IndexCtx.handleDecl(MD, D->getLocation(), SymbolRoleSet(), {}, ImplD); } if (ObjCMethodDecl *MD = PD->getSetterMethodDecl()) { if (MD->isPropertyAccessor() && - !hasUserDefined(MD, cast(D->getDeclContext()))) - IndexCtx.handleSynthesizedObjCMethod(MD, D->getLocation(), - D->getLexicalDeclContext()); + !hasUserDefined(MD, ImplD)) + IndexCtx.handleDecl(MD, D->getLocation(), SymbolRoleSet(), {}, ImplD); } return true; } bool VisitNamespaceDecl(const NamespaceDecl *D) { - IndexCtx.handleNamespace(D); + if (!IndexCtx.handleDecl(D)) + return false; IndexCtx.indexDeclContext(D); return true; } bool VisitUsingDecl(const UsingDecl *D) { - // FIXME: Parent for the following is CXIdxEntity_Unexposed with no USR, - // we should do better. + const DeclContext *DC = D->getDeclContext()->getRedeclContext(); + const NamedDecl *Parent = dyn_cast(DC); - IndexCtx.indexNestedNameSpecifierLoc(D->getQualifierLoc(), D); + IndexCtx.indexNestedNameSpecifierLoc(D->getQualifierLoc(), Parent, + D->getLexicalDeclContext()); for (const auto *I : D->shadows()) - IndexCtx.handleReference(I->getUnderlyingDecl(), D->getLocation(), D, - D->getLexicalDeclContext()); + IndexCtx.handleReference(I->getUnderlyingDecl(), D->getLocation(), Parent, + D->getLexicalDeclContext(), SymbolRoleSet()); return true; } bool VisitUsingDirectiveDecl(const UsingDirectiveDecl *D) { - // FIXME: Parent for the following is CXIdxEntity_Unexposed with no USR, - // we should do better. - - IndexCtx.indexNestedNameSpecifierLoc(D->getQualifierLoc(), D); - IndexCtx.handleReference(D->getNominatedNamespaceAsWritten(), - D->getLocation(), D, D->getLexicalDeclContext()); - return true; - } - - bool VisitClassTemplateDecl(const ClassTemplateDecl *D) { - IndexCtx.handleClassTemplate(D); - if (D->isThisDeclarationADefinition()) - IndexCtx.indexDeclContext(D->getTemplatedDecl()); - return true; + const DeclContext *DC = D->getDeclContext()->getRedeclContext(); + const NamedDecl *Parent = dyn_cast(DC); + + IndexCtx.indexNestedNameSpecifierLoc(D->getQualifierLoc(), Parent, + D->getLexicalDeclContext()); + return IndexCtx.handleReference(D->getNominatedNamespaceAsWritten(), + D->getLocation(), Parent, + D->getLexicalDeclContext(), + SymbolRoleSet()); } bool VisitClassTemplateSpecializationDecl(const ClassTemplateSpecializationDecl *D) { // FIXME: Notify subsequent callbacks if info comes from implicit // instantiation. - if (D->isThisDeclarationADefinition() && - (IndexCtx.shouldIndexImplicitTemplateInsts() || - !IndexCtx.isTemplateImplicitInstantiation(D))) + if (D->isThisDeclarationADefinition()) IndexCtx.indexTagDecl(D); return true; } - bool VisitFunctionTemplateDecl(const FunctionTemplateDecl *D) { - IndexCtx.handleFunctionTemplate(D); - FunctionDecl *FD = D->getTemplatedDecl(); - handleDeclarator(FD, D); - if (FD->isThisDeclarationADefinition()) { - const Stmt *Body = FD->getBody(); - if (Body) { - IndexCtx.indexBody(Body, D, FD); - } - } - return true; + bool VisitTemplateDecl(const TemplateDecl *D) { + // FIXME: Template parameters. + return Visit(D->getTemplatedDecl()); } - bool VisitTypeAliasTemplateDecl(const TypeAliasTemplateDecl *D) { - IndexCtx.handleTypeAliasTemplate(D); - IndexCtx.indexTypeSourceInfo(D->getTemplatedDecl()->getTypeSourceInfo(), D); + bool VisitFriendDecl(const FriendDecl *D) { + if (auto ND = D->getFriendDecl()) { + // FIXME: Ignore a class template in a dependent context, these are not + // linked properly with their redeclarations, ending up with duplicate + // USRs. + // See comment "Friend templates are visible in fairly strange ways." in + // SemaTemplate.cpp which precedes code that prevents the friend template + // from becoming visible from the enclosing context. + if (isa(ND) && D->getDeclContext()->isDependentContext()) + return true; + return Visit(ND); + } + if (auto Ty = D->getFriendType()) { + IndexCtx.indexTypeSourceInfo(Ty, cast(D->getDeclContext())); + } return true; } bool VisitImportDecl(const ImportDecl *D) { - IndexCtx.importedModule(D); - return true; + return IndexCtx.importedModule(D); } }; } // anonymous namespace -void IndexingContext::indexDecl(const Decl *D) { +bool IndexingContext::indexDecl(const Decl *D) { if (D->isImplicit() && shouldIgnoreIfImplicit(D)) - return; + return true; - bool Handled = IndexingDeclVisitor(*this).Visit(D); - if (!Handled && isa(D)) - indexDeclContext(cast(D)); + if (isTemplateImplicitInstantiation(D)) + return true; + + IndexingDeclVisitor Visitor(*this); + bool ShouldContinue = Visitor.Visit(D); + if (!ShouldContinue) + return false; + + if (!Visitor.Handled && isa(D)) + return indexDeclContext(cast(D)); + + return true; } -void IndexingContext::indexDeclContext(const DeclContext *DC) { +bool IndexingContext::indexDeclContext(const DeclContext *DC) { for (const auto *I : DC->decls()) - indexDecl(I); + if (!indexDecl(I)) + return false; + return true; } -void IndexingContext::indexTopLevelDecl(const Decl *D) { - if (isNotFromSourceFile(D->getLocation())) - return; +bool IndexingContext::indexTopLevelDecl(const Decl *D) { + if (D->getLocation().isInvalid()) + return true; if (isa(D)) - return; // Wait for the objc container. + return true; // Wait for the objc container. - indexDecl(D); + return indexDecl(D); } -void IndexingContext::indexDeclGroupRef(DeclGroupRef DG) { +bool IndexingContext::indexDeclGroupRef(DeclGroupRef DG) { for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) - indexTopLevelDecl(*I); + if (!indexTopLevelDecl(*I)) + return false; + return true; } diff --git a/lib/Index/IndexSymbol.cpp b/lib/Index/IndexSymbol.cpp new file mode 100644 index 00000000000..c7c3c8c4d64 --- /dev/null +++ b/lib/Index/IndexSymbol.cpp @@ -0,0 +1,187 @@ +//===--- IndexSymbol.cpp - Types and functions for indexing symbols -------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/IndexSymbol.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" + +using namespace clang; +using namespace clang::index; + +SymbolInfo index::getSymbolInfo(const Decl *D) { + assert(D); + SymbolInfo Info; + Info.Kind = SymbolKind::Unknown; + Info.TemplateKind = SymbolCXXTemplateKind::NonTemplate; + Info.Lang = SymbolLanguage::C; + + if (const TagDecl *TD = dyn_cast(D)) { + switch (TD->getTagKind()) { + case TTK_Struct: + Info.Kind = SymbolKind::Struct; break; + case TTK_Union: + Info.Kind = SymbolKind::Union; break; + case TTK_Class: + Info.Kind = SymbolKind::CXXClass; + Info.Lang = SymbolLanguage::CXX; + break; + case TTK_Interface: + Info.Kind = SymbolKind::CXXInterface; + Info.Lang = SymbolLanguage::CXX; + break; + case TTK_Enum: + Info.Kind = SymbolKind::Enum; break; + } + + if (const CXXRecordDecl *CXXRec = dyn_cast(D)) + if (!CXXRec->isCLike()) + Info.Lang = SymbolLanguage::CXX; + + if (isa(D)) { + Info.TemplateKind = SymbolCXXTemplateKind::TemplatePartialSpecialization; + } else if (isa(D)) { + Info.TemplateKind = SymbolCXXTemplateKind::TemplateSpecialization; + } + + } else { + switch (D->getKind()) { + case Decl::Typedef: + Info.Kind = SymbolKind::Typedef; break; + case Decl::Function: + Info.Kind = SymbolKind::Function; + break; + case Decl::ParmVar: + Info.Kind = SymbolKind::Variable; + break; + case Decl::Var: + Info.Kind = SymbolKind::Variable; + if (isa(D->getDeclContext())) { + Info.Kind = SymbolKind::CXXStaticVariable; + Info.Lang = SymbolLanguage::CXX; + } + break; + case Decl::Field: + Info.Kind = SymbolKind::Field; + if (const CXXRecordDecl * + CXXRec = dyn_cast(D->getDeclContext())) { + if (!CXXRec->isCLike()) + Info.Lang = SymbolLanguage::CXX; + } + break; + case Decl::EnumConstant: + Info.Kind = SymbolKind::EnumConstant; break; + case Decl::ObjCInterface: + case Decl::ObjCImplementation: + Info.Kind = SymbolKind::ObjCClass; + Info.Lang = SymbolLanguage::ObjC; + break; + case Decl::ObjCProtocol: + Info.Kind = SymbolKind::ObjCProtocol; + Info.Lang = SymbolLanguage::ObjC; + break; + case Decl::ObjCCategory: + case Decl::ObjCCategoryImpl: + Info.Kind = SymbolKind::ObjCCategory; + Info.Lang = SymbolLanguage::ObjC; + break; + case Decl::ObjCMethod: + if (cast(D)->isInstanceMethod()) + Info.Kind = SymbolKind::ObjCInstanceMethod; + else + Info.Kind = SymbolKind::ObjCClassMethod; + Info.Lang = SymbolLanguage::ObjC; + break; + case Decl::ObjCProperty: + Info.Kind = SymbolKind::ObjCProperty; + Info.Lang = SymbolLanguage::ObjC; + break; + case Decl::ObjCIvar: + Info.Kind = SymbolKind::ObjCIvar; + Info.Lang = SymbolLanguage::ObjC; + break; + case Decl::Namespace: + Info.Kind = SymbolKind::CXXNamespace; + Info.Lang = SymbolLanguage::CXX; + break; + case Decl::NamespaceAlias: + Info.Kind = SymbolKind::CXXNamespaceAlias; + Info.Lang = SymbolLanguage::CXX; + break; + case Decl::CXXConstructor: + Info.Kind = SymbolKind::CXXConstructor; + Info.Lang = SymbolLanguage::CXX; + break; + case Decl::CXXDestructor: + Info.Kind = SymbolKind::CXXDestructor; + Info.Lang = SymbolLanguage::CXX; + break; + case Decl::CXXConversion: + Info.Kind = SymbolKind::CXXConversionFunction; + Info.Lang = SymbolLanguage::CXX; + break; + case Decl::CXXMethod: { + const CXXMethodDecl *MD = cast(D); + if (MD->isStatic()) + Info.Kind = SymbolKind::CXXStaticMethod; + else + Info.Kind = SymbolKind::CXXInstanceMethod; + Info.Lang = SymbolLanguage::CXX; + break; + } + case Decl::ClassTemplate: + Info.Kind = SymbolKind::CXXClass; + Info.TemplateKind = SymbolCXXTemplateKind::Template; + break; + case Decl::FunctionTemplate: + Info.Kind = SymbolKind::Function; + Info.TemplateKind = SymbolCXXTemplateKind::Template; + if (const CXXMethodDecl *MD = dyn_cast_or_null( + cast(D)->getTemplatedDecl())) { + if (isa(MD)) + Info.Kind = SymbolKind::CXXConstructor; + else if (isa(MD)) + Info.Kind = SymbolKind::CXXDestructor; + else if (isa(MD)) + Info.Kind = SymbolKind::CXXConversionFunction; + else { + if (MD->isStatic()) + Info.Kind = SymbolKind::CXXStaticMethod; + else + Info.Kind = SymbolKind::CXXInstanceMethod; + } + } + break; + case Decl::TypeAliasTemplate: + Info.Kind = SymbolKind::CXXTypeAlias; + Info.TemplateKind = SymbolCXXTemplateKind::Template; + break; + case Decl::TypeAlias: + Info.Kind = SymbolKind::CXXTypeAlias; + Info.Lang = SymbolLanguage::CXX; + break; + default: + break; + } + } + + if (Info.Kind == SymbolKind::Unknown) + return Info; + + if (const FunctionDecl *FD = dyn_cast(D)) { + if (FD->getTemplatedKind() == + FunctionDecl::TK_FunctionTemplateSpecialization) + Info.TemplateKind = SymbolCXXTemplateKind::TemplateSpecialization; + } + + if (Info.TemplateKind != SymbolCXXTemplateKind::NonTemplate) + Info.Lang = SymbolLanguage::CXX; + + return Info; +} diff --git a/tools/libclang/IndexTypeSourceInfo.cpp b/lib/Index/IndexTypeSourceInfo.cpp similarity index 52% rename from tools/libclang/IndexTypeSourceInfo.cpp rename to lib/Index/IndexTypeSourceInfo.cpp index 9666052ed18..619a9a48bef 100644 --- a/tools/libclang/IndexTypeSourceInfo.cpp +++ b/lib/Index/IndexTypeSourceInfo.cpp @@ -1,4 +1,4 @@ -//===- CIndexHigh.cpp - Higher level API functions ------------------------===// +//===- IndexTypeSourceInfo.cpp - Indexing types ---------------------------===// // // The LLVM Compiler Infrastructure // @@ -11,7 +11,7 @@ #include "clang/AST/RecursiveASTVisitor.h" using namespace clang; -using namespace cxindex; +using namespace index; namespace { @@ -19,20 +19,58 @@ class TypeIndexer : public RecursiveASTVisitor { IndexingContext &IndexCtx; const NamedDecl *Parent; const DeclContext *ParentDC; + bool IsBase; + SmallVector Relations; + + typedef RecursiveASTVisitor base; public: TypeIndexer(IndexingContext &indexCtx, const NamedDecl *parent, - const DeclContext *DC) - : IndexCtx(indexCtx), Parent(parent), ParentDC(DC) { } + const DeclContext *DC, bool isBase) + : IndexCtx(indexCtx), Parent(parent), ParentDC(DC), IsBase(isBase) { + if (IsBase) { + assert(Parent); + Relations.emplace_back((unsigned)SymbolRole::RelationBaseOf, Parent); + } + } bool shouldWalkTypesOfTypeLocs() const { return false; } bool VisitTypedefTypeLoc(TypedefTypeLoc TL) { - IndexCtx.handleReference(TL.getTypedefNameDecl(), TL.getNameLoc(), - Parent, ParentDC); + return IndexCtx.handleReference(TL.getTypedefNameDecl(), TL.getNameLoc(), + Parent, ParentDC, SymbolRoleSet(), + Relations); + } + +#define TRY_TO(CALL_EXPR) \ + do { \ + if (!CALL_EXPR) \ + return false; \ + } while (0) + + bool traverseParamVarHelper(ParmVarDecl *D) { + TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc())); + if (D->getTypeSourceInfo()) + TRY_TO(TraverseTypeLoc(D->getTypeSourceInfo()->getTypeLoc())); return true; } + bool TraverseParmVarDecl(ParmVarDecl *D) { + // Avoid visiting default arguments from the definition that were already + // visited in the declaration. + // FIXME: A free function definition can have default arguments. + // Avoiding double visitaiton of default arguments should be handled by the + // visitor probably with a bit in the AST to indicate if the attached + // default argument was 'inherited' or written in source. + if (auto FD = dyn_cast(D->getDeclContext())) { + if (FD->isThisDeclarationADefinition()) { + return traverseParamVarHelper(D); + } + } + + return base::TraverseParmVarDecl(D); + } + bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) { IndexCtx.indexNestedNameSpecifierLoc(NNS, Parent, ParentDC); return true; @@ -48,24 +86,20 @@ class TypeIndexer : public RecursiveASTVisitor { return true; } - if (D->getLocation() == TL.getNameLoc()) - IndexCtx.handleTagDecl(D); - else - IndexCtx.handleReference(D, TL.getNameLoc(), - Parent, ParentDC); - return true; + return IndexCtx.handleReference(D, TL.getNameLoc(), + Parent, ParentDC, SymbolRoleSet(), + Relations); } bool VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc TL) { - IndexCtx.handleReference(TL.getIFaceDecl(), TL.getNameLoc(), - Parent, ParentDC); - return true; + return IndexCtx.handleReference(TL.getIFaceDecl(), TL.getNameLoc(), + Parent, ParentDC, SymbolRoleSet()); } bool VisitObjCObjectTypeLoc(ObjCObjectTypeLoc TL) { for (unsigned i = 0, e = TL.getNumProtocols(); i != e; ++i) { IndexCtx.handleReference(TL.getProtocol(i), TL.getProtocolLoc(i), - Parent, ParentDC); + Parent, ParentDC, SymbolRoleSet()); } return true; } @@ -75,11 +109,11 @@ class TypeIndexer : public RecursiveASTVisitor { if (IndexCtx.shouldIndexImplicitTemplateInsts()) { if (CXXRecordDecl *RD = T->getAsCXXRecordDecl()) IndexCtx.handleReference(RD, TL.getTemplateNameLoc(), - Parent, ParentDC); + Parent, ParentDC, SymbolRoleSet(), Relations); } else { if (const TemplateDecl *D = T->getTemplateName().getAsTemplateDecl()) IndexCtx.handleReference(D, TL.getTemplateNameLoc(), - Parent, ParentDC); + Parent, ParentDC, SymbolRoleSet(), Relations); } } return true; @@ -95,22 +129,24 @@ class TypeIndexer : public RecursiveASTVisitor { void IndexingContext::indexTypeSourceInfo(TypeSourceInfo *TInfo, const NamedDecl *Parent, - const DeclContext *DC) { + const DeclContext *DC, + bool isBase) { if (!TInfo || TInfo->getTypeLoc().isNull()) return; - indexTypeLoc(TInfo->getTypeLoc(), Parent, DC); + indexTypeLoc(TInfo->getTypeLoc(), Parent, DC, isBase); } void IndexingContext::indexTypeLoc(TypeLoc TL, const NamedDecl *Parent, - const DeclContext *DC) { + const DeclContext *DC, + bool isBase) { if (TL.isNull()) return; if (!DC) DC = Parent->getLexicalDeclContext(); - TypeIndexer(*this, Parent, DC).TraverseTypeLoc(TL); + TypeIndexer(*this, Parent, DC, isBase).TraverseTypeLoc(TL); } void IndexingContext::indexNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS, @@ -134,11 +170,11 @@ void IndexingContext::indexNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS, case NestedNameSpecifier::Namespace: handleReference(NNS.getNestedNameSpecifier()->getAsNamespace(), - Loc, Parent, DC); + Loc, Parent, DC, SymbolRoleSet()); break; case NestedNameSpecifier::NamespaceAlias: handleReference(NNS.getNestedNameSpecifier()->getAsNamespaceAlias(), - Loc, Parent, DC); + Loc, Parent, DC, SymbolRoleSet()); break; case NestedNameSpecifier::TypeSpec: @@ -149,8 +185,18 @@ void IndexingContext::indexNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS, } void IndexingContext::indexTagDecl(const TagDecl *D) { - if (handleTagDecl(D)) { - if (D->isThisDeclarationADefinition()) + if (!shouldIndexFunctionLocalSymbols() && isFunctionLocalDecl(D)) + return; + + if (handleDecl(D)) { + if (D->isThisDeclarationADefinition()) { + indexNestedNameSpecifierLoc(D->getQualifierLoc(), D); + if (auto CXXRD = dyn_cast(D)) { + for (const auto &I : CXXRD->bases()) { + indexTypeSourceInfo(I.getTypeSourceInfo(), CXXRD, CXXRD, /*isBase=*/true); + } + } indexDeclContext(D); + } } } diff --git a/lib/Index/IndexingAction.cpp b/lib/Index/IndexingAction.cpp new file mode 100644 index 00000000000..3f7ef43e7dc --- /dev/null +++ b/lib/Index/IndexingAction.cpp @@ -0,0 +1,140 @@ +//===- IndexingAction.cpp - Frontend index action -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/IndexingAction.h" +#include "clang/Index/IndexDataConsumer.h" +#include "IndexingContext.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Frontend/MultiplexConsumer.h" +#include "clang/Lex/Preprocessor.h" + +using namespace clang; +using namespace clang::index; + +void IndexDataConsumer::_anchor() {} + +bool IndexDataConsumer::handleDeclOccurence(const Decl *D, SymbolRoleSet Roles, + ArrayRef Relations, + FileID FID, unsigned Offset, + ASTNodeInfo ASTNode) { + return true; +} + +bool IndexDataConsumer::handleMacroOccurence(const IdentifierInfo *Name, + const MacroInfo *MI, SymbolRoleSet Roles, + FileID FID, unsigned Offset) { + return true; +} + +bool IndexDataConsumer::handleModuleOccurence(const ImportDecl *ImportD, + SymbolRoleSet Roles, + FileID FID, unsigned Offset) { + return true; +} + +namespace { + +class IndexASTConsumer : public ASTConsumer { + IndexingContext &IndexCtx; + +public: + IndexASTConsumer(IndexingContext &IndexCtx) + : IndexCtx(IndexCtx) {} + +protected: + void Initialize(ASTContext &Context) override { + IndexCtx.setASTContext(Context); + } + + bool HandleTopLevelDecl(DeclGroupRef DG) override { + return IndexCtx.indexDeclGroupRef(DG); + } + + void HandleInterestingDecl(DeclGroupRef DG) override { + // Ignore deserialized decls. + } + + void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) override { + IndexCtx.indexDeclGroupRef(DG); + } + + void HandleTranslationUnit(ASTContext &Ctx) override { + } +}; + +class IndexAction : public WrapperFrontendAction { + IndexingOptions IndexOpts; + std::shared_ptr DataConsumer; + std::unique_ptr IndexCtx; + +public: + IndexAction(std::unique_ptr WrappedAction, + std::shared_ptr DataConsumer, + IndexingOptions Opts) + : WrapperFrontendAction(std::move(WrappedAction)), + IndexOpts(Opts), + DataConsumer(std::move(DataConsumer)) {} + +protected: + std::unique_ptr CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override; + void EndSourceFileAction() override; +}; + +} // anonymous namespace + +void IndexAction::EndSourceFileAction() { + // Invoke wrapped action's method. + WrapperFrontendAction::EndSourceFileAction(); + + bool IndexActionFailed = !IndexCtx; + if (!IndexActionFailed) + DataConsumer->finish(); +} + +std::unique_ptr +IndexAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + auto OtherConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile); + if (!OtherConsumer) + return nullptr; + + IndexCtx.reset(new IndexingContext(IndexOpts, *DataConsumer)); + + std::vector> Consumers; + Consumers.push_back(std::move(OtherConsumer)); + Consumers.push_back(llvm::make_unique(*IndexCtx)); + return llvm::make_unique(std::move(Consumers)); +} + +std::unique_ptr +index::createIndexingAction(std::unique_ptr WrappedAction, + std::shared_ptr DataConsumer, + IndexingOptions Opts) { + return llvm::make_unique(std::move(WrappedAction), + std::move(DataConsumer), + Opts); +} + + +static bool topLevelDeclVisitor(void *context, const Decl *D) { + IndexingContext &IndexCtx = *static_cast(context); + return IndexCtx.indexTopLevelDecl(D); +} + +static void indexTranslationUnit(ASTUnit &Unit, IndexingContext &IndexCtx) { + Unit.visitLocalTopLevelDecls(&IndexCtx, topLevelDeclVisitor); +} + +void index::indexASTUnit(ASTUnit &Unit, + std::shared_ptr DataConsumer, + IndexingOptions Opts) { + IndexingContext IndexCtx(Opts, *DataConsumer); + IndexCtx.setASTContext(Unit.getASTContext()); + indexTranslationUnit(Unit, IndexCtx); +} diff --git a/lib/Index/IndexingContext.cpp b/lib/Index/IndexingContext.cpp new file mode 100644 index 00000000000..91fbbfb2684 --- /dev/null +++ b/lib/Index/IndexingContext.cpp @@ -0,0 +1,325 @@ +//===- IndexingContext.cpp - Indexing context data ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "IndexingContext.h" +#include "clang/Index/IndexDataConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/DeclObjC.h" +#include "clang/Basic/SourceManager.h" + +using namespace clang; +using namespace index; + +bool IndexingContext::shouldIndexFunctionLocalSymbols() const { + return IndexOpts.IndexFunctionLocals; +} + +bool IndexingContext::handleDecl(const Decl *D, + SymbolRoleSet Roles, + ArrayRef Relations) { + return handleDeclOccurrence(D, D->getLocation(), /*IsRef=*/false, + cast(D->getDeclContext()), Roles, Relations, + nullptr, nullptr, D->getDeclContext()); +} + +bool IndexingContext::handleDecl(const Decl *D, SourceLocation Loc, + SymbolRoleSet Roles, + ArrayRef Relations, + const DeclContext *DC) { + if (!DC) + DC = D->getDeclContext(); + return handleDeclOccurrence(D, Loc, /*IsRef=*/false, cast(DC), + Roles, Relations, + nullptr, nullptr, DC); +} + +bool IndexingContext::handleReference(const NamedDecl *D, SourceLocation Loc, + const NamedDecl *Parent, + const DeclContext *DC, + SymbolRoleSet Roles, + ArrayRef Relations, + const Expr *RefE, + const Decl *RefD) { + if (!shouldIndexFunctionLocalSymbols() && isFunctionLocalDecl(D)) + return true; + + if (isa(D) || isa(D)) + return true; + + return handleDeclOccurrence(D, Loc, /*IsRef=*/true, Parent, Roles, Relations, + RefE, RefD, DC); +} + +bool IndexingContext::importedModule(const ImportDecl *ImportD) { + SourceLocation Loc = ImportD->getLocation(); + SourceManager &SM = Ctx->getSourceManager(); + Loc = SM.getFileLoc(Loc); + if (Loc.isInvalid()) + return true; + + FileID FID; + unsigned Offset; + std::tie(FID, Offset) = SM.getDecomposedLoc(Loc); + if (FID.isInvalid()) + return true; + + bool Invalid = false; + const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, &Invalid); + if (Invalid || !SEntry.isFile()) + return true; + + if (SEntry.getFile().getFileCharacteristic() != SrcMgr::C_User) { + switch (IndexOpts.SystemSymbolFilter) { + case IndexingOptions::SystemSymbolFilterKind::None: + case IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly: + return true; + case IndexingOptions::SystemSymbolFilterKind::All: + break; + } + } + + SymbolRoleSet Roles{}; + if (ImportD->isImplicit()) + Roles |= (unsigned)SymbolRole::Implicit; + + return DataConsumer.handleModuleOccurence(ImportD, Roles, FID, Offset); +} + +bool IndexingContext::isFunctionLocalDecl(const Decl *D) { + assert(D); + + if (isa(D)) + return true; + + if (!D->getParentFunctionOrMethod()) + return false; + + if (const NamedDecl *ND = dyn_cast(D)) { + switch (ND->getFormalLinkage()) { + case NoLinkage: + case VisibleNoLinkage: + case InternalLinkage: + return true; + case UniqueExternalLinkage: + llvm_unreachable("Not a sema linkage"); + case ExternalLinkage: + return false; + } + } + + return true; +} + +bool IndexingContext::isTemplateImplicitInstantiation(const Decl *D) { + TemplateSpecializationKind TKind = TSK_Undeclared; + if (const ClassTemplateSpecializationDecl * + SD = dyn_cast(D)) { + TKind = SD->getSpecializationKind(); + } + if (const FunctionDecl *FD = dyn_cast(D)) { + TKind = FD->getTemplateSpecializationKind(); + } + switch (TKind) { + case TSK_Undeclared: + case TSK_ExplicitSpecialization: + return false; + case TSK_ImplicitInstantiation: + case TSK_ExplicitInstantiationDeclaration: + case TSK_ExplicitInstantiationDefinition: + return true; + } +} + +bool IndexingContext::shouldIgnoreIfImplicit(const Decl *D) { + if (isa(D)) + return false; + if (isa(D)) + return false; + if (isa(D)) + return false; + if (isa(D)) + return false; + if (isa(D)) + return false; + return true; +} + +static const Decl *adjustTemplateImplicitInstantiation(const Decl *D) { + if (const ClassTemplateSpecializationDecl * + SD = dyn_cast(D)) { + return SD->getTemplateInstantiationPattern(); + } + if (const FunctionDecl *FD = dyn_cast(D)) { + return FD->getTemplateInstantiationPattern(); + } + return nullptr; +} + +static bool isDeclADefinition(const Decl *D, ASTContext &Ctx) { + if (auto VD = dyn_cast(D)) + return VD->isThisDeclarationADefinition(Ctx); + + if (auto FD = dyn_cast(D)) + return FD->isThisDeclarationADefinition(); + + if (auto TD = dyn_cast(D)) + return TD->isThisDeclarationADefinition(); + + if (auto MD = dyn_cast(D)) + return MD->isThisDeclarationADefinition(); + + if (isa(D) || + isa(D) || + isa(D) || + isa(D) || + isa(D) || + isa(D)) + return true; + + return false; +} + +static const Decl *adjustParent(const Decl *Parent) { + if (!Parent) + return nullptr; + for (;; Parent = cast(Parent->getDeclContext())) { + if (isa(Parent)) + return nullptr; + if (isa(Parent) || isa(Parent)) + continue; + if (auto NS = dyn_cast(Parent)) { + if (NS->isAnonymousNamespace()) + continue; + } else if (auto EnumD = dyn_cast(Parent)) { + // Move enumerators under anonymous enum to the enclosing parent. + if (EnumD->getDeclName().isEmpty()) + continue; + } else if (auto RD = dyn_cast(Parent)) { + if (RD->isAnonymousStructOrUnion()) + continue; + } else if (auto FD = dyn_cast(Parent)) { + if (FD->getDeclName().isEmpty()) + continue; + } + return Parent; + } +} + +static const Decl *getCanonicalDecl(const Decl *D) { + D = D->getCanonicalDecl(); + if (auto TD = dyn_cast(D)) { + D = TD->getTemplatedDecl(); + assert(D->isCanonicalDecl()); + } + + return D; +} + +bool IndexingContext::handleDeclOccurrence(const Decl *D, SourceLocation Loc, + bool IsRef, const Decl *Parent, + SymbolRoleSet Roles, + ArrayRef Relations, + const Expr *OrigE, + const Decl *OrigD, + const DeclContext *ContainerDC) { + if (D->isImplicit() && !isa(D)) + return true; + if (!isa(D) || + (cast(D)->getDeclName().isEmpty() && + !isa(D) && !isa(D))) + return true; + + SourceManager &SM = Ctx->getSourceManager(); + Loc = SM.getFileLoc(Loc); + if (Loc.isInvalid()) + return true; + + FileID FID; + unsigned Offset; + std::tie(FID, Offset) = SM.getDecomposedLoc(Loc); + if (FID.isInvalid()) + return true; + + bool Invalid = false; + const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, &Invalid); + if (Invalid || !SEntry.isFile()) + return true; + + if (SEntry.getFile().getFileCharacteristic() != SrcMgr::C_User) { + switch (IndexOpts.SystemSymbolFilter) { + case IndexingOptions::SystemSymbolFilterKind::None: + return true; + case IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly: + if (IsRef) + return true; + break; + case IndexingOptions::SystemSymbolFilterKind::All: + break; + } + } + + if (isTemplateImplicitInstantiation(D)) { + if (!IsRef) + return true; + D = adjustTemplateImplicitInstantiation(D); + if (!D) + return true; + assert(!isTemplateImplicitInstantiation(D)); + } + + if (!OrigD) + OrigD = D; + + if (IsRef) + Roles |= (unsigned)SymbolRole::Reference; + else if (isDeclADefinition(D, *Ctx)) + Roles |= (unsigned)SymbolRole::Definition; + else + Roles |= (unsigned)SymbolRole::Declaration; + + D = getCanonicalDecl(D); + if (D->isImplicit() && !isa(D)) { + // operator new declarations will link to the implicit one as canonical. + return true; + } + Parent = adjustParent(Parent); + if (Parent) + Parent = getCanonicalDecl(Parent); + assert(!Parent || !Parent->isImplicit() || + isa(Parent) || isa(Parent)); + + SmallVector FinalRelations; + FinalRelations.reserve(Relations.size()+1); + + auto addRelation = [&](SymbolRelation Rel) { + auto It = std::find_if(FinalRelations.begin(), FinalRelations.end(), + [&](SymbolRelation Elem)->bool { + return Elem.RelatedSymbol == Rel.RelatedSymbol; + }); + if (It != FinalRelations.end()) { + It->Roles |= Rel.Roles; + } else { + FinalRelations.push_back(Rel); + } + Roles |= Rel.Roles; + }; + + if (!IsRef && Parent && !cast(Parent)->isFunctionOrMethod()) { + addRelation(SymbolRelation{(unsigned)SymbolRole::RelationChildOf, Parent}); + } + for (auto &Rel : Relations) { + addRelation(SymbolRelation(Rel.Roles, + Rel.RelatedSymbol->getCanonicalDecl())); + } + + IndexDataConsumer::ASTNodeInfo Node{ OrigE, OrigD, Parent, ContainerDC }; + return DataConsumer.handleDeclOccurence(D, Roles, FinalRelations, FID, Offset, + Node); +} diff --git a/lib/Index/IndexingContext.h b/lib/Index/IndexingContext.h new file mode 100644 index 00000000000..774650547f8 --- /dev/null +++ b/lib/Index/IndexingContext.h @@ -0,0 +1,121 @@ +//===- IndexingContext.h - Indexing context data ----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_INDEX_INDEXINGCONTEXT_H +#define LLVM_CLANG_LIB_INDEX_INDEXINGCONTEXT_H + +#include "clang/Basic/LLVM.h" +#include "clang/Index/IndexSymbol.h" +#include "clang/Index/IndexingAction.h" +#include "llvm/ADT/ArrayRef.h" + +namespace clang { + class ASTContext; + class Decl; + class DeclGroupRef; + class ImportDecl; + class TagDecl; + class TypeSourceInfo; + class NamedDecl; + class ObjCMethodDecl; + class DeclContext; + class NestedNameSpecifierLoc; + class Stmt; + class Expr; + class TypeLoc; + class SourceLocation; + +namespace index { + class IndexDataConsumer; + +class IndexingContext { + IndexingOptions IndexOpts; + IndexDataConsumer &DataConsumer; + ASTContext *Ctx = nullptr; + +public: + IndexingContext(IndexingOptions IndexOpts, IndexDataConsumer &DataConsumer) + : IndexOpts(IndexOpts), DataConsumer(DataConsumer) {} + + const IndexingOptions &getIndexOpts() const { return IndexOpts; } + IndexDataConsumer &getDataConsumer() { return DataConsumer; } + + void setASTContext(ASTContext &ctx) { Ctx = &ctx; } + + bool shouldSuppressRefs() const { + return false; + } + + bool shouldIndexFunctionLocalSymbols() const; + + bool shouldIndexImplicitTemplateInsts() const { + return false; + } + + static bool isFunctionLocalDecl(const Decl *D); + static bool isTemplateImplicitInstantiation(const Decl *D); + + bool handleDecl(const Decl *D, SymbolRoleSet Roles = SymbolRoleSet(), + ArrayRef Relations = {}); + + bool handleDecl(const Decl *D, SourceLocation Loc, + SymbolRoleSet Roles = SymbolRoleSet(), + ArrayRef Relations = {}, + const DeclContext *DC = nullptr); + + bool handleReference(const NamedDecl *D, SourceLocation Loc, + const NamedDecl *Parent, + const DeclContext *DC, + SymbolRoleSet Roles, + ArrayRef Relations = {}, + const Expr *RefE = nullptr, + const Decl *RefD = nullptr); + + bool importedModule(const ImportDecl *ImportD); + + bool indexDecl(const Decl *D); + + void indexTagDecl(const TagDecl *D); + + void indexTypeSourceInfo(TypeSourceInfo *TInfo, const NamedDecl *Parent, + const DeclContext *DC = nullptr, + bool isBase = false); + + void indexTypeLoc(TypeLoc TL, const NamedDecl *Parent, + const DeclContext *DC = nullptr, + bool isBase = false); + + void indexNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS, + const NamedDecl *Parent, + const DeclContext *DC = nullptr); + + bool indexDeclContext(const DeclContext *DC); + + void indexBody(const Stmt *S, const NamedDecl *Parent, + const DeclContext *DC = nullptr); + + bool indexTopLevelDecl(const Decl *D); + bool indexDeclGroupRef(DeclGroupRef DG); + +private: + bool shouldIgnoreIfImplicit(const Decl *D); + + bool handleDeclOccurrence(const Decl *D, SourceLocation Loc, + bool IsRef, const Decl *Parent, + SymbolRoleSet Roles, + ArrayRef Relations, + const Expr *RefE, + const Decl *RefD, + const DeclContext *ContainerDC); +}; + +} // end namespace index +} // end namespace clang + +#endif diff --git a/tools/libclang/CMakeLists.txt b/tools/libclang/CMakeLists.txt index 56d053447f6..0dfad30ac28 100644 --- a/tools/libclang/CMakeLists.txt +++ b/tools/libclang/CMakeLists.txt @@ -11,17 +11,14 @@ set(SOURCES CIndexer.cpp CXComment.cpp CXCursor.cpp + CXIndexDataConsumer.cpp CXCompilationDatabase.cpp CXLoadedDiagnostic.cpp CXSourceLocation.cpp CXStoredDiagnostic.cpp CXString.cpp CXType.cpp - IndexBody.cpp - IndexDecl.cpp - IndexTypeSourceInfo.cpp Indexing.cpp - IndexingContext.cpp ADDITIONAL_HEADERS CIndexDiagnostic.h diff --git a/tools/libclang/IndexingContext.cpp b/tools/libclang/CXIndexDataConsumer.cpp similarity index 70% rename from tools/libclang/IndexingContext.cpp rename to tools/libclang/CXIndexDataConsumer.cpp index 7f65412f989..f6f8e306794 100644 --- a/tools/libclang/IndexingContext.cpp +++ b/tools/libclang/CXIndexDataConsumer.cpp @@ -1,4 +1,4 @@ -//===- IndexingContext.cpp - Higher level API functions -------------------===// +//===- CXIndexDataConsumer.cpp - Index data consumer for libclang----------===// // // The LLVM Compiler Infrastructure // @@ -7,21 +7,219 @@ // //===----------------------------------------------------------------------===// -#include "IndexingContext.h" +#include "CXIndexDataConsumer.h" #include "CIndexDiagnostic.h" #include "CXTranslationUnit.h" #include "clang/AST/Attr.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" +#include "clang/AST/DeclVisitor.h" #include "clang/Frontend/ASTUnit.h" using namespace clang; +using namespace clang::index; using namespace cxindex; using namespace cxcursor; -IndexingContext::ObjCProtocolListInfo::ObjCProtocolListInfo( +namespace { +class IndexingDeclVisitor : public ConstDeclVisitor { + CXIndexDataConsumer &DataConsumer; + SourceLocation DeclLoc; + const DeclContext *LexicalDC; + +public: + IndexingDeclVisitor(CXIndexDataConsumer &dataConsumer, SourceLocation Loc, + const DeclContext *lexicalDC) + : DataConsumer(dataConsumer), DeclLoc(Loc), LexicalDC(lexicalDC) { } + + bool VisitFunctionDecl(const FunctionDecl *D) { + DataConsumer.handleFunction(D); + return true; + } + + bool VisitVarDecl(const VarDecl *D) { + DataConsumer.handleVar(D); + return true; + } + + bool VisitFieldDecl(const FieldDecl *D) { + DataConsumer.handleField(D); + return true; + } + + bool VisitMSPropertyDecl(const MSPropertyDecl *D) { + return true; + } + + bool VisitEnumConstantDecl(const EnumConstantDecl *D) { + DataConsumer.handleEnumerator(D); + return true; + } + + bool VisitTypedefNameDecl(const TypedefNameDecl *D) { + DataConsumer.handleTypedefName(D); + return true; + } + + bool VisitTagDecl(const TagDecl *D) { + DataConsumer.handleTagDecl(D); + return true; + } + + bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *D) { + DataConsumer.handleObjCInterface(D); + return true; + } + + bool VisitObjCProtocolDecl(const ObjCProtocolDecl *D) { + DataConsumer.handleObjCProtocol(D); + return true; + } + + bool VisitObjCImplementationDecl(const ObjCImplementationDecl *D) { + DataConsumer.handleObjCImplementation(D); + return true; + } + + bool VisitObjCCategoryDecl(const ObjCCategoryDecl *D) { + DataConsumer.handleObjCCategory(D); + return true; + } + + bool VisitObjCCategoryImplDecl(const ObjCCategoryImplDecl *D) { + DataConsumer.handleObjCCategoryImpl(D); + return true; + } + + bool VisitObjCMethodDecl(const ObjCMethodDecl *D) { + if (D->getDeclContext() != LexicalDC) + DataConsumer.handleSynthesizedObjCMethod(D, DeclLoc, LexicalDC); + else + DataConsumer.handleObjCMethod(D); + return true; + } + + bool VisitObjCPropertyDecl(const ObjCPropertyDecl *D) { + DataConsumer.handleObjCProperty(D); + return true; + } + + bool VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *D) { + DataConsumer.handleSynthesizedObjCProperty(D); + return true; + } + + bool VisitNamespaceDecl(const NamespaceDecl *D) { + DataConsumer.handleNamespace(D); + return true; + } + + bool VisitUsingDecl(const UsingDecl *D) { + return true; + } + + bool VisitUsingDirectiveDecl(const UsingDirectiveDecl *D) { + return true; + } + + bool VisitClassTemplateDecl(const ClassTemplateDecl *D) { + DataConsumer.handleClassTemplate(D); + return true; + } + + bool VisitClassTemplateSpecializationDecl(const + ClassTemplateSpecializationDecl *D) { + DataConsumer.handleTagDecl(D); + return true; + } + + bool VisitFunctionTemplateDecl(const FunctionTemplateDecl *D) { + DataConsumer.handleFunctionTemplate(D); + return true; + } + + bool VisitTypeAliasTemplateDecl(const TypeAliasTemplateDecl *D) { + DataConsumer.handleTypeAliasTemplate(D); + return true; + } + + bool VisitImportDecl(const ImportDecl *D) { + DataConsumer.importedModule(D); + return true; + } +}; +} + +bool CXIndexDataConsumer::handleDeclOccurence(const Decl *D, + SymbolRoleSet Roles, + ArrayRef Relations, + FileID FID, unsigned Offset, + ASTNodeInfo ASTNode) { + SourceLocation Loc = getASTContext().getSourceManager() + .getLocForStartOfFile(FID).getLocWithOffset(Offset); + + if (Roles & (unsigned)SymbolRole::Reference) { + const NamedDecl *ND = dyn_cast(D); + if (!ND) + return true; + + if (auto *ObjCID = dyn_cast_or_null(ASTNode.OrigD)) { + if (!ObjCID->isThisDeclarationADefinition() && + ObjCID->getLocation() == Loc) { + // The libclang API treats this as ObjCClassRef declaration. + IndexingDeclVisitor(*this, Loc, nullptr).Visit(ObjCID); + return true; + } + } + + CXIdxEntityRefKind Kind = CXIdxEntityRef_Direct; + if (Roles & (unsigned)SymbolRole::Implicit) { + Kind = CXIdxEntityRef_Implicit; + } + + CXCursor Cursor; + if (ASTNode.OrigE) { + Cursor = cxcursor::MakeCXCursor(ASTNode.OrigE, + cast(ASTNode.ContainerDC), + getCXTU()); + } else { + const NamedDecl *CursorD = dyn_cast_or_null(ASTNode.OrigD); + if (!CursorD) + CursorD = ND; + Cursor = getRefCursor(CursorD, Loc); + } + handleReference(ND, Loc, Cursor, + dyn_cast_or_null(ASTNode.Parent), + ASTNode.ContainerDC, ASTNode.OrigE, Kind); + + } else { + const DeclContext *DC = nullptr; + for (const auto &SymRel : Relations) { + if (SymRel.Roles & (unsigned)SymbolRole::RelationChildOf) + DC = dyn_cast(SymRel.RelatedSymbol); + } + IndexingDeclVisitor(*this, Loc, DC).Visit(ASTNode.OrigD); + } + + return !shouldAbort(); +} + +bool CXIndexDataConsumer::handleModuleOccurence(const ImportDecl *ImportD, + SymbolRoleSet Roles, + FileID FID, + unsigned Offset) { + IndexingDeclVisitor(*this, SourceLocation(), nullptr).Visit(ImportD); + return !shouldAbort(); +} + +void CXIndexDataConsumer::finish() { + indexDiagnostics(); +} + + +CXIndexDataConsumer::ObjCProtocolListInfo::ObjCProtocolListInfo( const ObjCProtocolList &ProtList, - IndexingContext &IdxCtx, + CXIndexDataConsumer &IdxCtx, ScratchAlloc &SA) { ObjCInterfaceDecl::protocol_loc_iterator LI = ProtList.loc_begin(); for (ObjCInterfaceDecl::protocol_iterator @@ -61,7 +259,7 @@ IBOutletCollectionInfo::IBOutletCollectionInfo( IBCollInfo.objcClass = nullptr; } -AttrListInfo::AttrListInfo(const Decl *D, IndexingContext &IdxCtx) +AttrListInfo::AttrListInfo(const Decl *D, CXIndexDataConsumer &IdxCtx) : SA(IdxCtx), ref_cnt(0) { if (!D->hasAttrs()) @@ -114,14 +312,14 @@ AttrListInfo::AttrListInfo(const Decl *D, IndexingContext &IdxCtx) } IntrusiveRefCntPtr -AttrListInfo::create(const Decl *D, IndexingContext &IdxCtx) { +AttrListInfo::create(const Decl *D, CXIndexDataConsumer &IdxCtx) { ScratchAlloc SA(IdxCtx); AttrListInfo *attrs = SA.allocate(); return new (attrs) AttrListInfo(D, IdxCtx); } -IndexingContext::CXXBasesListInfo::CXXBasesListInfo(const CXXRecordDecl *D, - IndexingContext &IdxCtx, +CXIndexDataConsumer::CXXBasesListInfo::CXXBasesListInfo(const CXXRecordDecl *D, + CXIndexDataConsumer &IdxCtx, ScratchAlloc &SA) { for (const auto &Base : D->bases()) { BaseEntities.push_back(EntityInfo()); @@ -155,7 +353,7 @@ IndexingContext::CXXBasesListInfo::CXXBasesListInfo(const CXXRecordDecl *D, CXBases.push_back(&BaseInfos[i]); } -SourceLocation IndexingContext::CXXBasesListInfo::getBaseLoc( +SourceLocation CXIndexDataConsumer::CXXBasesListInfo::getBaseLoc( const CXXBaseSpecifier &Base) const { SourceLocation Loc = Base.getSourceRange().getBegin(); TypeLoc TL; @@ -193,16 +391,16 @@ const char *ScratchAlloc::copyCStr(StringRef Str) { return buf; } -void IndexingContext::setASTContext(ASTContext &ctx) { +void CXIndexDataConsumer::setASTContext(ASTContext &ctx) { Ctx = &ctx; cxtu::getASTUnit(CXTU)->setASTContext(&ctx); } -void IndexingContext::setPreprocessor(Preprocessor &PP) { +void CXIndexDataConsumer::setPreprocessor(Preprocessor &PP) { cxtu::getASTUnit(CXTU)->setPreprocessor(&PP); } -bool IndexingContext::isFunctionLocalDecl(const Decl *D) { +bool CXIndexDataConsumer::isFunctionLocalDecl(const Decl *D) { assert(D); if (!D->getParentFunctionOrMethod()) @@ -224,13 +422,13 @@ bool IndexingContext::isFunctionLocalDecl(const Decl *D) { return true; } -bool IndexingContext::shouldAbort() { +bool CXIndexDataConsumer::shouldAbort() { if (!CB.abortQuery) return false; return CB.abortQuery(ClientData, nullptr); } -void IndexingContext::enteredMainFile(const FileEntry *File) { +void CXIndexDataConsumer::enteredMainFile(const FileEntry *File) { if (File && CB.enteredMainFile) { CXIdxClientFile idxFile = CB.enteredMainFile(ClientData, @@ -240,7 +438,7 @@ void IndexingContext::enteredMainFile(const FileEntry *File) { } } -void IndexingContext::ppIncludedFile(SourceLocation hashLoc, +void CXIndexDataConsumer::ppIncludedFile(SourceLocation hashLoc, StringRef filename, const FileEntry *File, bool isImport, bool isAngled, @@ -258,7 +456,7 @@ void IndexingContext::ppIncludedFile(SourceLocation hashLoc, FileMap[File] = idxFile; } -void IndexingContext::importedModule(const ImportDecl *ImportD) { +void CXIndexDataConsumer::importedModule(const ImportDecl *ImportD) { if (!CB.importedASTFile) return; @@ -277,7 +475,7 @@ void IndexingContext::importedModule(const ImportDecl *ImportD) { (void)astFile; } -void IndexingContext::importedPCH(const FileEntry *File) { +void CXIndexDataConsumer::importedPCH(const FileEntry *File) { if (!CB.importedASTFile) return; @@ -292,21 +490,29 @@ void IndexingContext::importedPCH(const FileEntry *File) { (void)astFile; } -void IndexingContext::startedTranslationUnit() { +void CXIndexDataConsumer::startedTranslationUnit() { CXIdxClientContainer idxCont = nullptr; if (CB.startedTranslationUnit) idxCont = CB.startedTranslationUnit(ClientData, nullptr); addContainerInMap(Ctx->getTranslationUnitDecl(), idxCont); } -void IndexingContext::handleDiagnosticSet(CXDiagnostic CXDiagSet) { +void CXIndexDataConsumer::indexDiagnostics() { + if (!hasDiagnosticCallback()) + return; + + CXDiagnosticSetImpl *DiagSet = cxdiag::lazyCreateDiags(getCXTU()); + handleDiagnosticSet(DiagSet); +} + +void CXIndexDataConsumer::handleDiagnosticSet(CXDiagnostic CXDiagSet) { if (!CB.diagnostic) return; CB.diagnostic(ClientData, CXDiagSet, nullptr); } -bool IndexingContext::handleDecl(const NamedDecl *D, +bool CXIndexDataConsumer::handleDecl(const NamedDecl *D, SourceLocation Loc, CXCursor Cursor, DeclInfo &DInfo, const DeclContext *LexicalDC, @@ -365,14 +571,14 @@ bool IndexingContext::handleDecl(const NamedDecl *D, return true; } -bool IndexingContext::handleObjCContainer(const ObjCContainerDecl *D, +bool CXIndexDataConsumer::handleObjCContainer(const ObjCContainerDecl *D, SourceLocation Loc, CXCursor Cursor, ObjCContainerDeclInfo &ContDInfo) { ContDInfo.ObjCContDeclInfo.declInfo = &ContDInfo; return handleDecl(D, Loc, Cursor, ContDInfo); } -bool IndexingContext::handleFunction(const FunctionDecl *D) { +bool CXIndexDataConsumer::handleFunction(const FunctionDecl *D) { bool isDef = D->isThisDeclarationADefinition(); bool isContainer = isDef; bool isSkipped = false; @@ -388,31 +594,31 @@ bool IndexingContext::handleFunction(const FunctionDecl *D) { return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleVar(const VarDecl *D) { +bool CXIndexDataConsumer::handleVar(const VarDecl *D) { DeclInfo DInfo(!D->isFirstDecl(), D->isThisDeclarationADefinition(), /*isContainer=*/false); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleField(const FieldDecl *D) { +bool CXIndexDataConsumer::handleField(const FieldDecl *D) { DeclInfo DInfo(/*isRedeclaration=*/false, /*isDefinition=*/true, /*isContainer=*/false); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleMSProperty(const MSPropertyDecl *D) { +bool CXIndexDataConsumer::handleMSProperty(const MSPropertyDecl *D) { DeclInfo DInfo(/*isRedeclaration=*/false, /*isDefinition=*/true, /*isContainer=*/false); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleEnumerator(const EnumConstantDecl *D) { +bool CXIndexDataConsumer::handleEnumerator(const EnumConstantDecl *D) { DeclInfo DInfo(/*isRedeclaration=*/false, /*isDefinition=*/true, /*isContainer=*/false); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleTagDecl(const TagDecl *D) { +bool CXIndexDataConsumer::handleTagDecl(const TagDecl *D) { if (const CXXRecordDecl *CXXRD = dyn_cast(D)) return handleCXXRecordDecl(CXXRD, D); @@ -421,13 +627,13 @@ bool IndexingContext::handleTagDecl(const TagDecl *D) { return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleTypedefName(const TypedefNameDecl *D) { +bool CXIndexDataConsumer::handleTypedefName(const TypedefNameDecl *D) { DeclInfo DInfo(!D->isFirstDecl(), /*isDefinition=*/true, /*isContainer=*/false); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleObjCInterface(const ObjCInterfaceDecl *D) { +bool CXIndexDataConsumer::handleObjCInterface(const ObjCInterfaceDecl *D) { // For @class forward declarations, suppress them the same way as references. if (!D->isThisDeclarationADefinition()) { if (shouldSuppressRefs() && markEntityOccurrenceInFile(D, D->getLocation())) @@ -475,7 +681,7 @@ bool IndexingContext::handleObjCInterface(const ObjCInterfaceDecl *D) { return handleObjCContainer(D, D->getLocation(), getCursor(D), InterInfo); } -bool IndexingContext::handleObjCImplementation( +bool CXIndexDataConsumer::handleObjCImplementation( const ObjCImplementationDecl *D) { ObjCContainerDeclInfo ContDInfo(/*isForwardRef=*/false, /*isRedeclaration=*/true, @@ -483,7 +689,7 @@ bool IndexingContext::handleObjCImplementation( return handleObjCContainer(D, D->getLocation(), getCursor(D), ContDInfo); } -bool IndexingContext::handleObjCProtocol(const ObjCProtocolDecl *D) { +bool CXIndexDataConsumer::handleObjCProtocol(const ObjCProtocolDecl *D) { if (!D->isThisDeclarationADefinition()) { if (shouldSuppressRefs() && markEntityOccurrenceInFile(D, D->getLocation())) return false; // already occurred. @@ -512,7 +718,7 @@ bool IndexingContext::handleObjCProtocol(const ObjCProtocolDecl *D) { return handleObjCContainer(D, D->getLocation(), getCursor(D), ProtInfo); } -bool IndexingContext::handleObjCCategory(const ObjCCategoryDecl *D) { +bool CXIndexDataConsumer::handleObjCCategory(const ObjCCategoryDecl *D) { ScratchAlloc SA(*this); ObjCCategoryDeclInfo CatDInfo(/*isImplementation=*/false); @@ -544,7 +750,7 @@ bool IndexingContext::handleObjCCategory(const ObjCCategoryDecl *D) { return handleObjCContainer(D, CategoryLoc, getCursor(D), CatDInfo); } -bool IndexingContext::handleObjCCategoryImpl(const ObjCCategoryImplDecl *D) { +bool CXIndexDataConsumer::handleObjCCategoryImpl(const ObjCCategoryImplDecl *D) { ScratchAlloc SA(*this); const ObjCCategoryDecl *CatD = D->getCategoryDecl(); @@ -573,7 +779,7 @@ bool IndexingContext::handleObjCCategoryImpl(const ObjCCategoryImplDecl *D) { return handleObjCContainer(D, CategoryLoc, getCursor(D), CatDInfo); } -bool IndexingContext::handleObjCMethod(const ObjCMethodDecl *D) { +bool CXIndexDataConsumer::handleObjCMethod(const ObjCMethodDecl *D) { bool isDef = D->isThisDeclarationADefinition(); bool isContainer = isDef; bool isSkipped = false; @@ -589,7 +795,7 @@ bool IndexingContext::handleObjCMethod(const ObjCMethodDecl *D) { return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleSynthesizedObjCProperty( +bool CXIndexDataConsumer::handleSynthesizedObjCProperty( const ObjCPropertyImplDecl *D) { ObjCPropertyDecl *PD = D->getPropertyDecl(); auto *DC = D->getDeclContext(); @@ -597,7 +803,7 @@ bool IndexingContext::handleSynthesizedObjCProperty( dyn_cast(DC), DC); } -bool IndexingContext::handleSynthesizedObjCMethod(const ObjCMethodDecl *D, +bool CXIndexDataConsumer::handleSynthesizedObjCMethod(const ObjCMethodDecl *D, SourceLocation Loc, const DeclContext *LexicalDC) { DeclInfo DInfo(/*isRedeclaration=*/true, /*isDefinition=*/true, @@ -605,7 +811,7 @@ bool IndexingContext::handleSynthesizedObjCMethod(const ObjCMethodDecl *D, return handleDecl(D, Loc, getCursor(D), DInfo, LexicalDC, LexicalDC); } -bool IndexingContext::handleObjCProperty(const ObjCPropertyDecl *D) { +bool CXIndexDataConsumer::handleObjCProperty(const ObjCPropertyDecl *D) { ScratchAlloc SA(*this); ObjCPropertyDeclInfo DInfo; @@ -630,31 +836,31 @@ bool IndexingContext::handleObjCProperty(const ObjCPropertyDecl *D) { return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleNamespace(const NamespaceDecl *D) { +bool CXIndexDataConsumer::handleNamespace(const NamespaceDecl *D) { DeclInfo DInfo(/*isRedeclaration=*/!D->isOriginalNamespace(), /*isDefinition=*/true, /*isContainer=*/true); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleClassTemplate(const ClassTemplateDecl *D) { +bool CXIndexDataConsumer::handleClassTemplate(const ClassTemplateDecl *D) { return handleCXXRecordDecl(D->getTemplatedDecl(), D); } -bool IndexingContext::handleFunctionTemplate(const FunctionTemplateDecl *D) { +bool CXIndexDataConsumer::handleFunctionTemplate(const FunctionTemplateDecl *D) { DeclInfo DInfo(/*isRedeclaration=*/!D->isCanonicalDecl(), /*isDefinition=*/D->isThisDeclarationADefinition(), /*isContainer=*/D->isThisDeclarationADefinition()); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleTypeAliasTemplate(const TypeAliasTemplateDecl *D) { +bool CXIndexDataConsumer::handleTypeAliasTemplate(const TypeAliasTemplateDecl *D) { DeclInfo DInfo(/*isRedeclaration=*/!D->isCanonicalDecl(), /*isDefinition=*/true, /*isContainer=*/false); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleReference(const NamedDecl *D, SourceLocation Loc, +bool CXIndexDataConsumer::handleReference(const NamedDecl *D, SourceLocation Loc, const NamedDecl *Parent, const DeclContext *DC, const Expr *E, @@ -667,7 +873,7 @@ bool IndexingContext::handleReference(const NamedDecl *D, SourceLocation Loc, return handleReference(D, Loc, Cursor, Parent, DC, E, Kind); } -bool IndexingContext::handleReference(const NamedDecl *D, SourceLocation Loc, +bool CXIndexDataConsumer::handleReference(const NamedDecl *D, SourceLocation Loc, CXCursor Cursor, const NamedDecl *Parent, const DeclContext *DC, @@ -713,7 +919,7 @@ bool IndexingContext::handleReference(const NamedDecl *D, SourceLocation Loc, return true; } -bool IndexingContext::isNotFromSourceFile(SourceLocation Loc) const { +bool CXIndexDataConsumer::isNotFromSourceFile(SourceLocation Loc) const { if (Loc.isInvalid()) return true; SourceManager &SM = Ctx->getSourceManager(); @@ -722,7 +928,7 @@ bool IndexingContext::isNotFromSourceFile(SourceLocation Loc) const { return SM.getFileEntryForID(FID) == nullptr; } -void IndexingContext::addContainerInMap(const DeclContext *DC, +void CXIndexDataConsumer::addContainerInMap(const DeclContext *DC, CXIdxClientContainer container) { if (!DC) return; @@ -741,7 +947,7 @@ void IndexingContext::addContainerInMap(const DeclContext *DC, ContainerMap.erase(I); } -CXIdxClientEntity IndexingContext::getClientEntity(const Decl *D) const { +CXIdxClientEntity CXIndexDataConsumer::getClientEntity(const Decl *D) const { if (!D) return nullptr; EntityMapTy::const_iterator I = EntityMap.find(D); @@ -750,13 +956,13 @@ CXIdxClientEntity IndexingContext::getClientEntity(const Decl *D) const { return I->second; } -void IndexingContext::setClientEntity(const Decl *D, CXIdxClientEntity client) { +void CXIndexDataConsumer::setClientEntity(const Decl *D, CXIdxClientEntity client) { if (!D) return; EntityMap[D] = client; } -bool IndexingContext::handleCXXRecordDecl(const CXXRecordDecl *RD, +bool CXIndexDataConsumer::handleCXXRecordDecl(const CXXRecordDecl *RD, const NamedDecl *OrigD) { if (RD->isThisDeclarationADefinition()) { ScratchAlloc SA(*this); @@ -789,7 +995,7 @@ bool IndexingContext::handleCXXRecordDecl(const CXXRecordDecl *RD, return handleDecl(OrigD, OrigD->getLocation(), getCursor(OrigD), DInfo); } -bool IndexingContext::markEntityOccurrenceInFile(const NamedDecl *D, +bool CXIndexDataConsumer::markEntityOccurrenceInFile(const NamedDecl *D, SourceLocation Loc) { if (!D || Loc.isInvalid()) return true; @@ -811,7 +1017,7 @@ bool IndexingContext::markEntityOccurrenceInFile(const NamedDecl *D, return !res.second; // already in map } -const NamedDecl *IndexingContext::getEntityDecl(const NamedDecl *D) const { +const NamedDecl *CXIndexDataConsumer::getEntityDecl(const NamedDecl *D) const { assert(D); D = cast(D->getCanonicalDecl()); @@ -834,7 +1040,7 @@ const NamedDecl *IndexingContext::getEntityDecl(const NamedDecl *D) const { } const DeclContext * -IndexingContext::getEntityContainer(const Decl *D) const { +CXIndexDataConsumer::getEntityContainer(const Decl *D) const { const DeclContext *DC = dyn_cast(D); if (DC) return DC; @@ -850,7 +1056,7 @@ IndexingContext::getEntityContainer(const Decl *D) const { } CXIdxClientContainer -IndexingContext::getClientContainerForDC(const DeclContext *DC) const { +CXIndexDataConsumer::getClientContainerForDC(const DeclContext *DC) const { if (!DC) return nullptr; @@ -861,7 +1067,7 @@ IndexingContext::getClientContainerForDC(const DeclContext *DC) const { return I->second; } -CXIdxClientFile IndexingContext::getIndexFile(const FileEntry *File) { +CXIdxClientFile CXIndexDataConsumer::getIndexFile(const FileEntry *File) { if (!File) return nullptr; @@ -872,17 +1078,17 @@ CXIdxClientFile IndexingContext::getIndexFile(const FileEntry *File) { return nullptr; } -CXIdxLoc IndexingContext::getIndexLoc(SourceLocation Loc) const { +CXIdxLoc CXIndexDataConsumer::getIndexLoc(SourceLocation Loc) const { CXIdxLoc idxLoc = { {nullptr, nullptr}, 0 }; if (Loc.isInvalid()) return idxLoc; - idxLoc.ptr_data[0] = const_cast(this); + idxLoc.ptr_data[0] = const_cast(this); idxLoc.int_data = Loc.getRawEncoding(); return idxLoc; } -void IndexingContext::translateLoc(SourceLocation Loc, +void CXIndexDataConsumer::translateLoc(SourceLocation Loc, CXIdxClientFile *indexFile, CXFile *file, unsigned *line, unsigned *column, unsigned *offset) { @@ -912,7 +1118,12 @@ void IndexingContext::translateLoc(SourceLocation Loc, *offset = FileOffset; } -void IndexingContext::getEntityInfo(const NamedDecl *D, +static CXIdxEntityKind getEntityKindFromSymbolKind(SymbolKind K); +static CXIdxEntityCXXTemplateKind +getEntityKindFromSymbolCXXTemplateKind(SymbolCXXTemplateKind K); +static CXIdxEntityLanguage getEntityLangFromSymbolLang(SymbolLanguage L); + +void CXIndexDataConsumer::getEntityInfo(const NamedDecl *D, EntityInfo &EntityInfo, ScratchAlloc &SA) { if (!D) @@ -922,9 +1133,12 @@ void IndexingContext::getEntityInfo(const NamedDecl *D, EntityInfo.cursor = getCursor(D); EntityInfo.Dcl = D; EntityInfo.IndexCtx = this; - EntityInfo.kind = CXIdxEntity_Unexposed; - EntityInfo.templateKind = CXIdxEntity_NonTemplate; - EntityInfo.lang = CXIdxEntityLang_C; + + SymbolInfo SymInfo = getSymbolInfo(D); + EntityInfo.kind = getEntityKindFromSymbolKind(SymInfo.Kind); + EntityInfo.templateKind = + getEntityKindFromSymbolCXXTemplateKind(SymInfo.TemplateKind); + EntityInfo.lang = getEntityLangFromSymbolLang(SymInfo.Lang); if (D->hasAttrs()) { EntityInfo.AttrList = AttrListInfo::create(D, *this); @@ -932,167 +1146,9 @@ void IndexingContext::getEntityInfo(const NamedDecl *D, EntityInfo.numAttributes = EntityInfo.AttrList->getNumAttrs(); } - if (const TagDecl *TD = dyn_cast(D)) { - switch (TD->getTagKind()) { - case TTK_Struct: - EntityInfo.kind = CXIdxEntity_Struct; break; - case TTK_Union: - EntityInfo.kind = CXIdxEntity_Union; break; - case TTK_Class: - EntityInfo.kind = CXIdxEntity_CXXClass; - EntityInfo.lang = CXIdxEntityLang_CXX; - break; - case TTK_Interface: - EntityInfo.kind = CXIdxEntity_CXXInterface; - EntityInfo.lang = CXIdxEntityLang_CXX; - break; - case TTK_Enum: - EntityInfo.kind = CXIdxEntity_Enum; break; - } - - if (const CXXRecordDecl *CXXRec = dyn_cast(D)) - if (!CXXRec->isCLike()) - EntityInfo.lang = CXIdxEntityLang_CXX; - - if (isa(D)) { - EntityInfo.templateKind = CXIdxEntity_TemplatePartialSpecialization; - } else if (isa(D)) { - EntityInfo.templateKind = CXIdxEntity_TemplateSpecialization; - } - - } else { - switch (D->getKind()) { - case Decl::Typedef: - EntityInfo.kind = CXIdxEntity_Typedef; break; - case Decl::Function: - EntityInfo.kind = CXIdxEntity_Function; - break; - case Decl::ParmVar: - EntityInfo.kind = CXIdxEntity_Variable; - break; - case Decl::Var: - EntityInfo.kind = CXIdxEntity_Variable; - if (isa(D->getDeclContext())) { - EntityInfo.kind = CXIdxEntity_CXXStaticVariable; - EntityInfo.lang = CXIdxEntityLang_CXX; - } - break; - case Decl::Field: - EntityInfo.kind = CXIdxEntity_Field; - if (const CXXRecordDecl * - CXXRec = dyn_cast(D->getDeclContext())) { - // FIXME: isPOD check is not sufficient, a POD can contain methods, - // we want a isCStructLike check. - if (!CXXRec->isPOD()) - EntityInfo.lang = CXIdxEntityLang_CXX; - } - break; - case Decl::EnumConstant: - EntityInfo.kind = CXIdxEntity_EnumConstant; break; - case Decl::ObjCInterface: - EntityInfo.kind = CXIdxEntity_ObjCClass; - EntityInfo.lang = CXIdxEntityLang_ObjC; - break; - case Decl::ObjCProtocol: - EntityInfo.kind = CXIdxEntity_ObjCProtocol; - EntityInfo.lang = CXIdxEntityLang_ObjC; - break; - case Decl::ObjCCategory: - EntityInfo.kind = CXIdxEntity_ObjCCategory; - EntityInfo.lang = CXIdxEntityLang_ObjC; - break; - case Decl::ObjCMethod: - if (cast(D)->isInstanceMethod()) - EntityInfo.kind = CXIdxEntity_ObjCInstanceMethod; - else - EntityInfo.kind = CXIdxEntity_ObjCClassMethod; - EntityInfo.lang = CXIdxEntityLang_ObjC; - break; - case Decl::ObjCProperty: - EntityInfo.kind = CXIdxEntity_ObjCProperty; - EntityInfo.lang = CXIdxEntityLang_ObjC; - break; - case Decl::ObjCIvar: - EntityInfo.kind = CXIdxEntity_ObjCIvar; - EntityInfo.lang = CXIdxEntityLang_ObjC; - break; - case Decl::Namespace: - EntityInfo.kind = CXIdxEntity_CXXNamespace; - EntityInfo.lang = CXIdxEntityLang_CXX; - break; - case Decl::NamespaceAlias: - EntityInfo.kind = CXIdxEntity_CXXNamespaceAlias; - EntityInfo.lang = CXIdxEntityLang_CXX; - break; - case Decl::CXXConstructor: - EntityInfo.kind = CXIdxEntity_CXXConstructor; - EntityInfo.lang = CXIdxEntityLang_CXX; - break; - case Decl::CXXDestructor: - EntityInfo.kind = CXIdxEntity_CXXDestructor; - EntityInfo.lang = CXIdxEntityLang_CXX; - break; - case Decl::CXXConversion: - EntityInfo.kind = CXIdxEntity_CXXConversionFunction; - EntityInfo.lang = CXIdxEntityLang_CXX; - break; - case Decl::CXXMethod: { - const CXXMethodDecl *MD = cast(D); - if (MD->isStatic()) - EntityInfo.kind = CXIdxEntity_CXXStaticMethod; - else - EntityInfo.kind = CXIdxEntity_CXXInstanceMethod; - EntityInfo.lang = CXIdxEntityLang_CXX; - break; - } - case Decl::ClassTemplate: - EntityInfo.kind = CXIdxEntity_CXXClass; - EntityInfo.templateKind = CXIdxEntity_Template; - break; - case Decl::FunctionTemplate: - EntityInfo.kind = CXIdxEntity_Function; - EntityInfo.templateKind = CXIdxEntity_Template; - if (const CXXMethodDecl *MD = dyn_cast_or_null( - cast(D)->getTemplatedDecl())) { - if (isa(MD)) - EntityInfo.kind = CXIdxEntity_CXXConstructor; - else if (isa(MD)) - EntityInfo.kind = CXIdxEntity_CXXDestructor; - else if (isa(MD)) - EntityInfo.kind = CXIdxEntity_CXXConversionFunction; - else { - if (MD->isStatic()) - EntityInfo.kind = CXIdxEntity_CXXStaticMethod; - else - EntityInfo.kind = CXIdxEntity_CXXInstanceMethod; - } - } - break; - case Decl::TypeAliasTemplate: - EntityInfo.kind = CXIdxEntity_CXXTypeAlias; - EntityInfo.templateKind = CXIdxEntity_Template; - break; - case Decl::TypeAlias: - EntityInfo.kind = CXIdxEntity_CXXTypeAlias; - EntityInfo.lang = CXIdxEntityLang_CXX; - break; - default: - break; - } - } - if (EntityInfo.kind == CXIdxEntity_Unexposed) return; - if (const FunctionDecl *FD = dyn_cast(D)) { - if (FD->getTemplatedKind() == - FunctionDecl::TK_FunctionTemplateSpecialization) - EntityInfo.templateKind = CXIdxEntity_TemplateSpecialization; - } - - if (EntityInfo.templateKind != CXIdxEntity_NonTemplate) - EntityInfo.lang = CXIdxEntityLang_CXX; - if (IdentifierInfo *II = D->getIdentifier()) { EntityInfo.name = SA.toCStr(II->getName()); @@ -1119,14 +1175,14 @@ void IndexingContext::getEntityInfo(const NamedDecl *D, } } -void IndexingContext::getContainerInfo(const DeclContext *DC, +void CXIndexDataConsumer::getContainerInfo(const DeclContext *DC, ContainerInfo &ContInfo) { ContInfo.cursor = getCursor(cast(DC)); ContInfo.DC = DC; ContInfo.IndexCtx = this; } -CXCursor IndexingContext::getRefCursor(const NamedDecl *D, SourceLocation Loc) { +CXCursor CXIndexDataConsumer::getRefCursor(const NamedDecl *D, SourceLocation Loc) { if (const TypeDecl *TD = dyn_cast(D)) return MakeCursorTypeRef(TD, Loc, CXTU); if (const ObjCInterfaceDecl *ID = dyn_cast(D)) @@ -1147,7 +1203,7 @@ CXCursor IndexingContext::getRefCursor(const NamedDecl *D, SourceLocation Loc) { return clang_getNullCursor(); } -bool IndexingContext::shouldIgnoreIfImplicit(const Decl *D) { +bool CXIndexDataConsumer::shouldIgnoreIfImplicit(const Decl *D) { if (isa(D)) return false; if (isa(D)) @@ -1161,7 +1217,7 @@ bool IndexingContext::shouldIgnoreIfImplicit(const Decl *D) { return true; } -bool IndexingContext::isTemplateImplicitInstantiation(const Decl *D) { +bool CXIndexDataConsumer::isTemplateImplicitInstantiation(const Decl *D) { if (const ClassTemplateSpecializationDecl * SD = dyn_cast(D)) { return SD->getSpecializationKind() == TSK_ImplicitInstantiation; @@ -1171,3 +1227,60 @@ bool IndexingContext::isTemplateImplicitInstantiation(const Decl *D) { } return false; } + +static CXIdxEntityKind getEntityKindFromSymbolKind(SymbolKind K) { + switch (K) { + case SymbolKind::Unknown: + case SymbolKind::Module: + case SymbolKind::Macro: + return CXIdxEntity_Unexposed; + + case SymbolKind::Enum: return CXIdxEntity_Enum; + case SymbolKind::Struct: return CXIdxEntity_Struct; + case SymbolKind::Union: return CXIdxEntity_Union; + case SymbolKind::Typedef: return CXIdxEntity_Typedef; + case SymbolKind::Function: return CXIdxEntity_Function; + case SymbolKind::Variable: return CXIdxEntity_Variable; + case SymbolKind::Field: return CXIdxEntity_Field; + case SymbolKind::EnumConstant: return CXIdxEntity_EnumConstant; + case SymbolKind::ObjCClass: return CXIdxEntity_ObjCClass; + case SymbolKind::ObjCProtocol: return CXIdxEntity_ObjCProtocol; + case SymbolKind::ObjCCategory: return CXIdxEntity_ObjCCategory; + case SymbolKind::ObjCInstanceMethod: return CXIdxEntity_ObjCInstanceMethod; + case SymbolKind::ObjCClassMethod: return CXIdxEntity_ObjCClassMethod; + case SymbolKind::ObjCProperty: return CXIdxEntity_ObjCProperty; + case SymbolKind::ObjCIvar: return CXIdxEntity_ObjCIvar; + case SymbolKind::CXXClass: return CXIdxEntity_CXXClass; + case SymbolKind::CXXNamespace: return CXIdxEntity_CXXNamespace; + case SymbolKind::CXXNamespaceAlias: return CXIdxEntity_CXXNamespaceAlias; + case SymbolKind::CXXStaticVariable: return CXIdxEntity_CXXStaticVariable; + case SymbolKind::CXXStaticMethod: return CXIdxEntity_CXXStaticMethod; + case SymbolKind::CXXInstanceMethod: return CXIdxEntity_CXXInstanceMethod; + case SymbolKind::CXXConstructor: return CXIdxEntity_CXXConstructor; + case SymbolKind::CXXDestructor: return CXIdxEntity_CXXDestructor; + case SymbolKind::CXXConversionFunction: + return CXIdxEntity_CXXConversionFunction; + case SymbolKind::CXXTypeAlias: return CXIdxEntity_CXXTypeAlias; + case SymbolKind::CXXInterface: return CXIdxEntity_CXXInterface; + } +} + +static CXIdxEntityCXXTemplateKind +getEntityKindFromSymbolCXXTemplateKind(SymbolCXXTemplateKind K) { + switch (K) { + case SymbolCXXTemplateKind::NonTemplate: return CXIdxEntity_NonTemplate; + case SymbolCXXTemplateKind::Template: return CXIdxEntity_Template; + case SymbolCXXTemplateKind::TemplatePartialSpecialization: + return CXIdxEntity_TemplatePartialSpecialization; + case SymbolCXXTemplateKind::TemplateSpecialization: + return CXIdxEntity_TemplateSpecialization; + } +} + +static CXIdxEntityLanguage getEntityLangFromSymbolLang(SymbolLanguage L) { + switch (L) { + case SymbolLanguage::C: return CXIdxEntityLang_C; + case SymbolLanguage::ObjC: return CXIdxEntityLang_ObjC; + case SymbolLanguage::CXX: return CXIdxEntityLang_CXX; + } +} diff --git a/tools/libclang/IndexingContext.h b/tools/libclang/CXIndexDataConsumer.h similarity index 91% rename from tools/libclang/IndexingContext.h rename to tools/libclang/CXIndexDataConsumer.h index d1d62c90d45..308fa79488d 100644 --- a/tools/libclang/IndexingContext.h +++ b/tools/libclang/CXIndexDataConsumer.h @@ -1,4 +1,4 @@ -//===- IndexingContext.h - Higher level API functions -----------*- C++ -*-===// +//===- CXIndexDataConsumer.h - Index data consumer for libclang--*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -7,11 +7,12 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_TOOLS_LIBCLANG_INDEXINGCONTEXT_H -#define LLVM_CLANG_TOOLS_LIBCLANG_INDEXINGCONTEXT_H +#ifndef LLVM_CLANG_TOOLS_LIBCLANG_CXINDEXDATACONSUMER_H +#define LLVM_CLANG_TOOLS_LIBCLANG_CXINDEXDATACONSUMER_H #include "CXCursor.h" #include "Index_Internal.h" +#include "clang/Index/IndexDataConsumer.h" #include "clang/AST/DeclGroup.h" #include "clang/AST/DeclObjC.h" #include "llvm/ADT/DenseSet.h" @@ -27,14 +28,14 @@ namespace clang { class ClassTemplateSpecializationDecl; namespace cxindex { - class IndexingContext; + class CXIndexDataConsumer; class AttrListInfo; class ScratchAlloc { - IndexingContext &IdxCtx; + CXIndexDataConsumer &IdxCtx; public: - explicit ScratchAlloc(IndexingContext &indexCtx); + explicit ScratchAlloc(CXIndexDataConsumer &indexCtx); ScratchAlloc(const ScratchAlloc &SA); ~ScratchAlloc(); @@ -48,7 +49,7 @@ class ScratchAlloc { struct EntityInfo : public CXIdxEntityInfo { const NamedDecl *Dcl; - IndexingContext *IndexCtx; + CXIndexDataConsumer *IndexCtx; IntrusiveRefCntPtr AttrList; EntityInfo() { @@ -60,7 +61,7 @@ struct EntityInfo : public CXIdxEntityInfo { struct ContainerInfo : public CXIdxContainerInfo { const DeclContext *DC; - IndexingContext *IndexCtx; + CXIndexDataConsumer *IndexCtx; }; struct DeclInfo : public CXIdxDeclInfo { @@ -248,10 +249,10 @@ class AttrListInfo { AttrListInfo(const AttrListInfo &) = delete; void operator=(const AttrListInfo &) = delete; public: - AttrListInfo(const Decl *D, IndexingContext &IdxCtx); + AttrListInfo(const Decl *D, CXIndexDataConsumer &IdxCtx); static IntrusiveRefCntPtr create(const Decl *D, - IndexingContext &IdxCtx); + CXIndexDataConsumer &IdxCtx); const CXIdxAttrInfo *const *getAttrs() const { if (CXAttrs.empty()) @@ -273,7 +274,7 @@ class AttrListInfo { } }; -class IndexingContext { +class CXIndexDataConsumer : public index::IndexDataConsumer { ASTContext *Ctx; CXClientData ClientData; IndexerCallbacks &CB; @@ -308,7 +309,7 @@ class IndexingContext { } ObjCProtocolListInfo(const ObjCProtocolList &ProtList, - IndexingContext &IdxCtx, + CXIndexDataConsumer &IdxCtx, ScratchAlloc &SA); }; @@ -323,7 +324,7 @@ class IndexingContext { unsigned getNumBases() const { return (unsigned)CXBases.size(); } CXXBasesListInfo(const CXXRecordDecl *D, - IndexingContext &IdxCtx, ScratchAlloc &SA); + CXIndexDataConsumer &IdxCtx, ScratchAlloc &SA); private: SourceLocation getBaseLoc(const CXXBaseSpecifier &Base) const; @@ -332,13 +333,14 @@ class IndexingContext { friend class AttrListInfo; public: - IndexingContext(CXClientData clientData, IndexerCallbacks &indexCallbacks, + CXIndexDataConsumer(CXClientData clientData, IndexerCallbacks &indexCallbacks, unsigned indexOptions, CXTranslationUnit cxTU) : Ctx(nullptr), ClientData(clientData), CB(indexCallbacks), IndexOptions(indexOptions), CXTU(cxTU), StrScratch(), StrAdapterCount(0) { } ASTContext &getASTContext() const { return *Ctx; } + CXTranslationUnit getCXTU() const { return CXTU; } void setASTContext(ASTContext &ctx); void setPreprocessor(Preprocessor &PP); @@ -391,6 +393,8 @@ class IndexingContext { void indexBody(const Stmt *S, const NamedDecl *Parent, const DeclContext *DC = nullptr); + void indexDiagnostics(); + void handleDiagnosticSet(CXDiagnosticSet CXDiagSet); bool handleFunction(const FunctionDecl *FD); @@ -458,6 +462,17 @@ class IndexingContext { static bool isTemplateImplicitInstantiation(const Decl *D); private: + bool handleDeclOccurence(const Decl *D, index::SymbolRoleSet Roles, + ArrayRef Relations, + FileID FID, unsigned Offset, + ASTNodeInfo ASTNode) override; + + bool handleModuleOccurence(const ImportDecl *ImportD, + index::SymbolRoleSet Roles, + FileID FID, unsigned Offset) override; + + void finish() override; + bool handleDecl(const NamedDecl *D, SourceLocation Loc, CXCursor Cursor, DeclInfo &DInfo, @@ -495,7 +510,7 @@ class IndexingContext { static bool shouldIgnoreIfImplicit(const Decl *D); }; -inline ScratchAlloc::ScratchAlloc(IndexingContext &idxCtx) : IdxCtx(idxCtx) { +inline ScratchAlloc::ScratchAlloc(CXIndexDataConsumer &idxCtx) : IdxCtx(idxCtx) { ++IdxCtx.StrAdapterCount; } inline ScratchAlloc::ScratchAlloc(const ScratchAlloc &SA) : IdxCtx(SA.IdxCtx) { diff --git a/tools/libclang/IndexBody.cpp b/tools/libclang/IndexBody.cpp deleted file mode 100644 index 58dc11722bf..00000000000 --- a/tools/libclang/IndexBody.cpp +++ /dev/null @@ -1,224 +0,0 @@ -//===- CIndexHigh.cpp - Higher level API functions ------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "IndexingContext.h" -#include "clang/AST/RecursiveASTVisitor.h" - -using namespace clang; -using namespace cxindex; - -namespace { - -class BodyIndexer : public RecursiveASTVisitor { - IndexingContext &IndexCtx; - const NamedDecl *Parent; - const DeclContext *ParentDC; - - typedef RecursiveASTVisitor base; -public: - BodyIndexer(IndexingContext &indexCtx, - const NamedDecl *Parent, const DeclContext *DC) - : IndexCtx(indexCtx), Parent(Parent), ParentDC(DC) { } - - bool shouldWalkTypesOfTypeLocs() const { return false; } - - bool TraverseTypeLoc(TypeLoc TL) { - IndexCtx.indexTypeLoc(TL, Parent, ParentDC); - return true; - } - - bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) { - IndexCtx.indexNestedNameSpecifierLoc(NNS, Parent, ParentDC); - return true; - } - - bool VisitDeclRefExpr(DeclRefExpr *E) { - IndexCtx.handleReference(E->getDecl(), E->getLocation(), - Parent, ParentDC, E); - return true; - } - - bool VisitMemberExpr(MemberExpr *E) { - IndexCtx.handleReference(E->getMemberDecl(), E->getMemberLoc(), - Parent, ParentDC, E); - return true; - } - - bool VisitObjCIvarRefExpr(ObjCIvarRefExpr *E) { - IndexCtx.handleReference(E->getDecl(), E->getLocation(), - Parent, ParentDC, E); - return true; - } - - bool VisitObjCMessageExpr(ObjCMessageExpr *E) { - if (ObjCMethodDecl *MD = E->getMethodDecl()) - IndexCtx.handleReference(MD, E->getSelectorStartLoc(), - Parent, ParentDC, E, - E->isImplicit() ? CXIdxEntityRef_Implicit - : CXIdxEntityRef_Direct); - return true; - } - - bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *E) { - if (E->isExplicitProperty()) - IndexCtx.handleReference(E->getExplicitProperty(), E->getLocation(), - Parent, ParentDC, E); - - // No need to do a handleReference for the objc method, because there will - // be a message expr as part of PseudoObjectExpr. - return true; - } - - bool VisitMSPropertyRefExpr(MSPropertyRefExpr *E) { - IndexCtx.handleReference(E->getPropertyDecl(), E->getMemberLoc(), Parent, - ParentDC, E, CXIdxEntityRef_Direct); - return true; - } - - bool VisitObjCProtocolExpr(ObjCProtocolExpr *E) { - IndexCtx.handleReference(E->getProtocol(), E->getProtocolIdLoc(), - Parent, ParentDC, E, CXIdxEntityRef_Direct); - return true; - } - - bool VisitObjCBoxedExpr(ObjCBoxedExpr *E) { - if (ObjCMethodDecl *MD = E->getBoxingMethod()) - IndexCtx.handleReference(MD, E->getLocStart(), - Parent, ParentDC, E, CXIdxEntityRef_Implicit); - return true; - } - - bool VisitObjCDictionaryLiteral(ObjCDictionaryLiteral *E) { - if (ObjCMethodDecl *MD = E->getDictWithObjectsMethod()) - IndexCtx.handleReference(MD, E->getLocStart(), - Parent, ParentDC, E, CXIdxEntityRef_Implicit); - return true; - } - - bool VisitObjCArrayLiteral(ObjCArrayLiteral *E) { - if (ObjCMethodDecl *MD = E->getArrayWithObjectsMethod()) - IndexCtx.handleReference(MD, E->getLocStart(), - Parent, ParentDC, E, CXIdxEntityRef_Implicit); - return true; - } - - bool VisitCXXConstructExpr(CXXConstructExpr *E) { - IndexCtx.handleReference(E->getConstructor(), E->getLocation(), - Parent, ParentDC, E); - return true; - } - - bool TraverseCXXOperatorCallExpr(CXXOperatorCallExpr *E, - DataRecursionQueue *Q = nullptr) { - if (E->getOperatorLoc().isInvalid()) - return true; // implicit. - return base::TraverseCXXOperatorCallExpr(E, Q); - } - - bool VisitDeclStmt(DeclStmt *S) { - if (IndexCtx.shouldIndexFunctionLocalSymbols()) { - IndexCtx.indexDeclGroupRef(S->getDeclGroup()); - return true; - } - - DeclGroupRef DG = S->getDeclGroup(); - for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) { - const Decl *D = *I; - if (!D) - continue; - if (!IndexCtx.isFunctionLocalDecl(D)) - IndexCtx.indexTopLevelDecl(D); - } - - return true; - } - - bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C) { - if (C->capturesThis() || C->capturesVLAType()) - return true; - - if (C->capturesVariable() && IndexCtx.shouldIndexFunctionLocalSymbols()) - IndexCtx.handleReference(C->getCapturedVar(), C->getLocation(), Parent, - ParentDC); - - // FIXME: Lambda init-captures. - return true; - } - - // RecursiveASTVisitor visits both syntactic and semantic forms, duplicating - // the things that we visit. Make sure to only visit the semantic form. - // Also visit things that are in the syntactic form but not the semantic one, - // for example the indices in DesignatedInitExprs. - bool TraverseInitListExpr(InitListExpr *S) { - - class SyntacticFormIndexer : - public RecursiveASTVisitor { - IndexingContext &IndexCtx; - const NamedDecl *Parent; - const DeclContext *ParentDC; - - public: - SyntacticFormIndexer(IndexingContext &indexCtx, - const NamedDecl *Parent, const DeclContext *DC) - : IndexCtx(indexCtx), Parent(Parent), ParentDC(DC) { } - - bool shouldWalkTypesOfTypeLocs() const { return false; } - - bool VisitDesignatedInitExpr(DesignatedInitExpr *E) { - for (DesignatedInitExpr::reverse_designators_iterator - D = E->designators_rbegin(), DEnd = E->designators_rend(); - D != DEnd; ++D) { - if (D->isFieldDesignator()) - IndexCtx.handleReference(D->getField(), D->getFieldLoc(), - Parent, ParentDC, E); - } - return true; - } - }; - - auto visitForm = [&](InitListExpr *Form) { - for (Stmt *SubStmt : Form->children()) { - if (!TraverseStmt(SubStmt)) - return false; - } - return true; - }; - - InitListExpr *SemaForm = S->isSemanticForm() ? S : S->getSemanticForm(); - InitListExpr *SyntaxForm = S->isSemanticForm() ? S->getSyntacticForm() : S; - - if (SemaForm) { - // Visit things present in syntactic form but not the semantic form. - if (SyntaxForm) { - SyntacticFormIndexer(IndexCtx, Parent, ParentDC).TraverseStmt(SyntaxForm); - } - return visitForm(SemaForm); - } - - // No semantic, try the syntactic. - if (SyntaxForm) { - return visitForm(SyntaxForm); - } - - return true; - } - -}; - -} // anonymous namespace - -void IndexingContext::indexBody(const Stmt *S, const NamedDecl *Parent, - const DeclContext *DC) { - if (!S) - return; - - if (!DC) - DC = Parent->getLexicalDeclContext(); - BodyIndexer(*this, Parent, DC).TraverseStmt(const_cast(S)); -} diff --git a/tools/libclang/Indexing.cpp b/tools/libclang/Indexing.cpp index 4929d6244e5..b37a8864dd6 100644 --- a/tools/libclang/Indexing.cpp +++ b/tools/libclang/Indexing.cpp @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// -#include "IndexingContext.h" +#include "CXIndexDataConsumer.h" #include "CIndexDiagnostic.h" #include "CIndexer.h" #include "CLog.h" @@ -16,12 +16,12 @@ #include "CXString.h" #include "CXTranslationUnit.h" #include "clang/AST/ASTConsumer.h" -#include "clang/AST/DeclVisitor.h" #include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/FrontendAction.h" #include "clang/Frontend/Utils.h" +#include "clang/Index/IndexingAction.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/PPCallbacks.h" #include "clang/Lex/PPConditionalDirectiveRecord.h" @@ -34,11 +34,10 @@ #include using namespace clang; +using namespace clang::index; using namespace cxtu; using namespace cxindex; -static void indexDiagnostics(CXTranslationUnit TU, IndexingContext &IdxCtx); - namespace { //===----------------------------------------------------------------------===// @@ -246,12 +245,12 @@ class TUSkipBodyControl { class IndexPPCallbacks : public PPCallbacks { Preprocessor &PP; - IndexingContext &IndexCtx; + CXIndexDataConsumer &DataConsumer; bool IsMainFileEntered; public: - IndexPPCallbacks(Preprocessor &PP, IndexingContext &indexCtx) - : PP(PP), IndexCtx(indexCtx), IsMainFileEntered(false) { } + IndexPPCallbacks(Preprocessor &PP, CXIndexDataConsumer &dataConsumer) + : PP(PP), DataConsumer(dataConsumer), IsMainFileEntered(false) { } void FileChanged(SourceLocation Loc, FileChangeReason Reason, SrcMgr::CharacteristicKind FileType, FileID PrevFID) override { @@ -263,7 +262,7 @@ class IndexPPCallbacks : public PPCallbacks { if (Loc == MainFileLoc && Reason == PPCallbacks::EnterFile) { IsMainFileEntered = true; - IndexCtx.enteredMainFile(SM.getFileEntryForID(SM.getMainFileID())); + DataConsumer.enteredMainFile(SM.getFileEntryForID(SM.getMainFileID())); } } @@ -274,7 +273,7 @@ class IndexPPCallbacks : public PPCallbacks { const Module *Imported) override { bool isImport = (IncludeTok.is(tok::identifier) && IncludeTok.getIdentifierInfo()->getPPKeywordID() == tok::pp_import); - IndexCtx.ppIncludedFile(HashLoc, FileName, File, isImport, IsAngled, + DataConsumer.ppIncludedFile(HashLoc, FileName, File, isImport, IsAngled, Imported); } @@ -301,18 +300,18 @@ class IndexPPCallbacks : public PPCallbacks { //===----------------------------------------------------------------------===// class IndexingConsumer : public ASTConsumer { - IndexingContext &IndexCtx; + CXIndexDataConsumer &DataConsumer; TUSkipBodyControl *SKCtrl; public: - IndexingConsumer(IndexingContext &indexCtx, TUSkipBodyControl *skCtrl) - : IndexCtx(indexCtx), SKCtrl(skCtrl) { } + IndexingConsumer(CXIndexDataConsumer &dataConsumer, TUSkipBodyControl *skCtrl) + : DataConsumer(dataConsumer), SKCtrl(skCtrl) { } // ASTConsumer Implementation void Initialize(ASTContext &Context) override { - IndexCtx.setASTContext(Context); - IndexCtx.startedTranslationUnit(); + DataConsumer.setASTContext(Context); + DataConsumer.startedTranslationUnit(); } void HandleTranslationUnit(ASTContext &Ctx) override { @@ -321,34 +320,7 @@ class IndexingConsumer : public ASTConsumer { } bool HandleTopLevelDecl(DeclGroupRef DG) override { - IndexCtx.indexDeclGroupRef(DG); - return !IndexCtx.shouldAbort(); - } - - /// \brief Handle the specified top-level declaration that occurred inside - /// and ObjC container. - void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) override { - IndexCtx.indexDeclGroupRef(DG); - } - - /// \brief This is called by the AST reader when deserializing things. - /// The default implementation forwards to HandleTopLevelDecl but we don't - /// care about them when indexing, so have an empty definition. - void HandleInterestingDecl(DeclGroupRef D) override {} - - void HandleTagDeclDefinition(TagDecl *D) override { - if (!IndexCtx.shouldIndexImplicitTemplateInsts()) - return; - - if (IndexCtx.isTemplateImplicitInstantiation(D)) - IndexCtx.indexDecl(D); - } - - void HandleCXXImplicitFunctionInstantiation(FunctionDecl *D) override { - if (!IndexCtx.shouldIndexImplicitTemplateInsts()) - return; - - IndexCtx.indexDecl(D); + return !DataConsumer.shouldAbort(); } bool shouldSkipFunctionBody(Decl *D) override { @@ -357,7 +329,7 @@ class IndexingConsumer : public ASTConsumer { return true; } - const SourceManager &SM = IndexCtx.getASTContext().getSourceManager(); + const SourceManager &SM = DataConsumer.getASTContext().getSourceManager(); SourceLocation Loc = D->getLocation(); if (Loc.isMacroID()) return false; @@ -398,34 +370,29 @@ class CaptureDiagnosticConsumer : public DiagnosticConsumer { //===----------------------------------------------------------------------===// class IndexingFrontendAction : public ASTFrontendAction { - IndexingContext IndexCtx; - CXTranslationUnit CXTU; + std::shared_ptr DataConsumer; SessionSkipBodyData *SKData; std::unique_ptr SKCtrl; public: - IndexingFrontendAction(CXClientData clientData, - IndexerCallbacks &indexCallbacks, - unsigned indexOptions, - CXTranslationUnit cxTU, + IndexingFrontendAction(std::shared_ptr dataConsumer, SessionSkipBodyData *skData) - : IndexCtx(clientData, indexCallbacks, indexOptions, cxTU), - CXTU(cxTU), SKData(skData) { } + : DataConsumer(dataConsumer), SKData(skData) { } std::unique_ptr CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override { PreprocessorOptions &PPOpts = CI.getPreprocessorOpts(); if (!PPOpts.ImplicitPCHInclude.empty()) { - IndexCtx.importedPCH( + DataConsumer->importedPCH( CI.getFileManager().getFile(PPOpts.ImplicitPCHInclude)); } - IndexCtx.setASTContext(CI.getASTContext()); + DataConsumer->setASTContext(CI.getASTContext()); Preprocessor &PP = CI.getPreprocessor(); - PP.addPPCallbacks(llvm::make_unique(PP, IndexCtx)); - IndexCtx.setPreprocessor(PP); + PP.addPPCallbacks(llvm::make_unique(PP, *DataConsumer)); + DataConsumer->setPreprocessor(PP); if (SKData) { auto *PPRec = new PPConditionalDirectiveRecord(PP.getSourceManager()); @@ -433,15 +400,11 @@ class IndexingFrontendAction : public ASTFrontendAction { SKCtrl = llvm::make_unique(*SKData, *PPRec, PP); } - return llvm::make_unique(IndexCtx, SKCtrl.get()); - } - - void EndSourceFileAction() override { - indexDiagnostics(CXTU, IndexCtx); + return llvm::make_unique(*DataConsumer, SKCtrl.get()); } TranslationUnitKind getTranslationUnitKind() override { - if (IndexCtx.shouldIndexImplicitTemplateInsts()) + if (DataConsumer->shouldIndexImplicitTemplateInsts()) return TU_Complete; else return TU_Prefix; @@ -453,6 +416,13 @@ class IndexingFrontendAction : public ASTFrontendAction { // clang_indexSourceFileUnit Implementation //===----------------------------------------------------------------------===// +static IndexingOptions getIndexingOptionsFromCXOptions(unsigned index_options) { + IndexingOptions IdxOpts; + if (index_options & CXIndexOpt_IndexFunctionLocalSymbols) + IdxOpts.IndexFunctionLocals = true; + return IdxOpts; +} + struct IndexSessionData { CXIndex CIdx; std::unique_ptr SkipBodyData; @@ -588,13 +558,17 @@ static CXErrorCode clang_indexSourceFile_Impl( if (SkipBodies) CInvok->getFrontendOpts().SkipFunctionBodies = true; - std::unique_ptr IndexAction; - IndexAction.reset(new IndexingFrontendAction(client_data, CB, - index_options, CXTU->getTU(), - SkipBodies ? IdxSession->SkipBodyData.get() : nullptr)); + auto DataConsumer = + std::make_shared(client_data, CB, index_options, + CXTU->getTU()); + auto InterAction = llvm::make_unique(DataConsumer, + SkipBodies ? IdxSession->SkipBodyData.get() : nullptr); + std::unique_ptr IndexAction; + IndexAction = createIndexingAction(std::move(InterAction), DataConsumer, + getIndexingOptionsFromCXOptions(index_options)); // Recover resources if we crash before exiting this method. - llvm::CrashRecoveryContextCleanupRegistrar + llvm::CrashRecoveryContextCleanupRegistrar IndexActionCleanup(IndexAction.get()); bool Persistent = requestedToGetTU; @@ -654,7 +628,7 @@ static CXErrorCode clang_indexSourceFile_Impl( // clang_indexTranslationUnit Implementation //===----------------------------------------------------------------------===// -static void indexPreprocessingRecord(ASTUnit &Unit, IndexingContext &IdxCtx) { +static void indexPreprocessingRecord(ASTUnit &Unit, CXIndexDataConsumer &IdxCtx) { Preprocessor &PP = Unit.getPreprocessor(); if (!PP.getPreprocessingRecord()) return; @@ -677,24 +651,6 @@ static void indexPreprocessingRecord(ASTUnit &Unit, IndexingContext &IdxCtx) { } } -static bool topLevelDeclVisitor(void *context, const Decl *D) { - IndexingContext &IdxCtx = *static_cast(context); - IdxCtx.indexTopLevelDecl(D); - return !IdxCtx.shouldAbort(); -} - -static void indexTranslationUnit(ASTUnit &Unit, IndexingContext &IdxCtx) { - Unit.visitLocalTopLevelDecls(&IdxCtx, topLevelDeclVisitor); -} - -static void indexDiagnostics(CXTranslationUnit TU, IndexingContext &IdxCtx) { - if (!IdxCtx.hasDiagnosticCallback()) - return; - - CXDiagnosticSetImpl *DiagSet = cxdiag::lazyCreateDiags(TU); - IdxCtx.handleDiagnosticSet(DiagSet); -} - static CXErrorCode clang_indexTranslationUnit_Impl( CXIndexAction idxAction, CXClientData client_data, IndexerCallbacks *client_index_callbacks, unsigned index_callbacks_size, @@ -718,19 +674,8 @@ static CXErrorCode clang_indexTranslationUnit_Impl( ? index_callbacks_size : sizeof(CB); memcpy(&CB, client_index_callbacks, ClientCBSize); - std::unique_ptr IndexCtx; - IndexCtx.reset(new IndexingContext(client_data, CB, index_options, TU)); - - // Recover resources if we crash before exiting this method. - llvm::CrashRecoveryContextCleanupRegistrar - IndexCtxCleanup(IndexCtx.get()); - - std::unique_ptr IndexConsumer; - IndexConsumer.reset(new IndexingConsumer(*IndexCtx, nullptr)); - - // Recover resources if we crash before exiting this method. - llvm::CrashRecoveryContextCleanupRegistrar - IndexConsumerCleanup(IndexConsumer.get()); + auto DataConsumer = std::make_shared(client_data, CB, + index_options, TU); ASTUnit *Unit = cxtu::getASTUnit(TU); if (!Unit) @@ -739,20 +684,21 @@ static CXErrorCode clang_indexTranslationUnit_Impl( ASTUnit::ConcurrencyCheck Check(*Unit); if (const FileEntry *PCHFile = Unit->getPCHFile()) - IndexCtx->importedPCH(PCHFile); + DataConsumer->importedPCH(PCHFile); FileManager &FileMgr = Unit->getFileManager(); if (Unit->getOriginalSourceFileName().empty()) - IndexCtx->enteredMainFile(nullptr); + DataConsumer->enteredMainFile(nullptr); else - IndexCtx->enteredMainFile(FileMgr.getFile(Unit->getOriginalSourceFileName())); + DataConsumer->enteredMainFile(FileMgr.getFile(Unit->getOriginalSourceFileName())); - IndexConsumer->Initialize(Unit->getASTContext()); + DataConsumer->setASTContext(Unit->getASTContext()); + DataConsumer->startedTranslationUnit(); - indexPreprocessingRecord(*Unit, *IndexCtx); - indexTranslationUnit(*Unit, *IndexCtx); - indexDiagnostics(TU, *IndexCtx); + indexPreprocessingRecord(*Unit, *DataConsumer); + indexASTUnit(*Unit, DataConsumer, getIndexingOptionsFromCXOptions(index_options)); + DataConsumer->indexDiagnostics(); return CXError_Success; } @@ -1037,9 +983,9 @@ void clang_indexLoc_getFileLocation(CXIdxLoc location, if (!location.ptr_data[0] || Loc.isInvalid()) return; - IndexingContext &IndexCtx = - *static_cast(location.ptr_data[0]); - IndexCtx.translateLoc(Loc, indexFile, file, line, column, offset); + CXIndexDataConsumer &DataConsumer = + *static_cast(location.ptr_data[0]); + DataConsumer.translateLoc(Loc, indexFile, file, line, column, offset); } CXSourceLocation clang_indexLoc_getCXSourceLocation(CXIdxLoc location) { @@ -1047,9 +993,9 @@ CXSourceLocation clang_indexLoc_getCXSourceLocation(CXIdxLoc location) { if (!location.ptr_data[0] || Loc.isInvalid()) return clang_getNullLocation(); - IndexingContext &IndexCtx = - *static_cast(location.ptr_data[0]); - return cxloc::translateSourceLocation(IndexCtx.getASTContext(), Loc); + CXIndexDataConsumer &DataConsumer = + *static_cast(location.ptr_data[0]); + return cxloc::translateSourceLocation(DataConsumer.getASTContext(), Loc); } } // end: extern "C" From 3e48783cec2acc6fd2c659b9f3b6c926156c4919 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Fri, 12 Feb 2016 23:30:07 +0000 Subject: [PATCH 351/742] [index] Add llvm/Support/DataTypes.h header to fix build failures in the bots. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260762 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Index/IndexSymbol.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/clang/Index/IndexSymbol.h b/include/clang/Index/IndexSymbol.h index 0849241b60e..7a505eac415 100644 --- a/include/clang/Index/IndexSymbol.h +++ b/include/clang/Index/IndexSymbol.h @@ -11,6 +11,7 @@ #define LLVM_CLANG_INDEX_INDEXSYMBOL_H #include "clang/Basic/LLVM.h" +#include "llvm/Support/DataTypes.h" namespace clang { class Decl; From d05f61519fc1e35dbac5e4538c03b577c7c40704 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Sat, 13 Feb 2016 01:24:19 +0000 Subject: [PATCH 352/742] [RecursiveASTVisitor] Introduce dataTraverseStmtPre()/dataTraverseStmtPost() to allow clients to do before/after actions during data recursive visitation. This should fix the asan bot that hits stack overflow in a couple of test/Index tests. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260785 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/RecursiveASTVisitor.h | 17 ++++++++++++++++- lib/Index/IndexBody.cpp | 10 +++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h index f1ae76b59c2..42e67517a61 100644 --- a/include/clang/AST/RecursiveASTVisitor.h +++ b/include/clang/AST/RecursiveASTVisitor.h @@ -163,6 +163,18 @@ template class RecursiveASTVisitor { /// otherwise (including when the argument is nullptr). bool TraverseStmt(Stmt *S, DataRecursionQueue *Queue = nullptr); + /// Invoked before visiting a statement or expression via data recursion. + /// + /// \returns false to skip visiting the node, true otherwise. + bool dataTraverseStmtPre(Stmt *S) { return true; } + + /// Invoked after visiting a statement or expression via data recursion. + /// This is not invoked if the previously invoked \c dataTraverseStmtPre + /// returned false. + /// + /// \returns false if the visitation was terminated early, true otherwise. + bool dataTraverseStmtPost(Stmt *S) { return true; } + /// \brief Recursively visit a type, by dispatching to /// Traverse*Type() based on the argument's getTypeClass() property. /// @@ -557,7 +569,10 @@ bool RecursiveASTVisitor::TraverseStmt(Stmt *S, Stmt *CurrS = LocalQueue.pop_back_val(); size_t N = LocalQueue.size(); - TRY_TO(dataTraverseNode(CurrS, &LocalQueue)); + if (getDerived().dataTraverseStmtPre(CurrS)) { + TRY_TO(dataTraverseNode(CurrS, &LocalQueue)); + TRY_TO(dataTraverseStmtPost(CurrS)); + } // Process new children in the order they were added. std::reverse(LocalQueue.begin() + N, LocalQueue.end()); } diff --git a/lib/Index/IndexBody.cpp b/lib/Index/IndexBody.cpp index 56fba28387d..f7164453db6 100644 --- a/lib/Index/IndexBody.cpp +++ b/lib/Index/IndexBody.cpp @@ -29,11 +29,15 @@ class BodyIndexer : public RecursiveASTVisitor { bool shouldWalkTypesOfTypeLocs() const { return false; } - bool TraverseStmt(Stmt *S) { + bool dataTraverseStmtPre(Stmt *S) { StmtStack.push_back(S); - bool ret = base::TraverseStmt(S); + return true; + } + + bool dataTraverseStmtPost(Stmt *S) { + assert(StmtStack.back() == S); StmtStack.pop_back(); - return ret; + return true; } bool TraverseTypeLoc(TypeLoc TL) { From 82f7c226cc1a23e3449483ac4c0abd05aa927d23 Mon Sep 17 00:00:00 2001 From: NAKAMURA Takumi Date: Sat, 13 Feb 2016 04:01:49 +0000 Subject: [PATCH 353/742] libclang/CMakeLists.txt: Prune IndexingContext.h out of ADDITIONAL_HEADERS. VS IDE uses it. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260802 91177308-0d34-0410-b5e6-96231b3b80d8 --- tools/libclang/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/libclang/CMakeLists.txt b/tools/libclang/CMakeLists.txt index 0dfad30ac28..20a2db312e6 100644 --- a/tools/libclang/CMakeLists.txt +++ b/tools/libclang/CMakeLists.txt @@ -30,7 +30,6 @@ set(SOURCES CXTranslationUnit.h CXType.h Index_Internal.h - IndexingContext.h ../../include/clang-c/Index.h ) From f1e543466a1f00de20edc3254746df2d571bae68 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Sat, 13 Feb 2016 05:17:15 +0000 Subject: [PATCH 354/742] [index] Change some default parameters to fix an MSVC ICE. Many thanks to Yunzhong Gao for tracking this down! git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260807 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Index/IndexingContext.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Index/IndexingContext.h b/lib/Index/IndexingContext.h index 774650547f8..600fc433b58 100644 --- a/lib/Index/IndexingContext.h +++ b/lib/Index/IndexingContext.h @@ -62,18 +62,18 @@ class IndexingContext { static bool isTemplateImplicitInstantiation(const Decl *D); bool handleDecl(const Decl *D, SymbolRoleSet Roles = SymbolRoleSet(), - ArrayRef Relations = {}); + ArrayRef Relations = None); bool handleDecl(const Decl *D, SourceLocation Loc, SymbolRoleSet Roles = SymbolRoleSet(), - ArrayRef Relations = {}, + ArrayRef Relations = None, const DeclContext *DC = nullptr); bool handleReference(const NamedDecl *D, SourceLocation Loc, const NamedDecl *Parent, const DeclContext *DC, SymbolRoleSet Roles, - ArrayRef Relations = {}, + ArrayRef Relations = None, const Expr *RefE = nullptr, const Decl *RefD = nullptr); From e1bcaafcbafc1856f8b113c333e8b159cce2bace Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Sat, 13 Feb 2016 21:46:50 +0000 Subject: [PATCH 355/742] [AST] Add a print() method in DeclarationName that accepts a PrintingPolicy. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260833 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/DeclarationName.h | 5 +- lib/AST/DeclarationName.cpp | 76 +++++++++++++++++++---------- 2 files changed, 54 insertions(+), 27 deletions(-) diff --git a/include/clang/AST/DeclarationName.h b/include/clang/AST/DeclarationName.h index 9482e83e81d..2d3cfe27a16 100644 --- a/include/clang/AST/DeclarationName.h +++ b/include/clang/AST/DeclarationName.h @@ -30,6 +30,7 @@ namespace clang { class IdentifierInfo; class MultiKeywordSelector; enum OverloadedOperatorKind : int; + struct PrintingPolicy; class QualType; class Type; class TypeSourceInfo; @@ -302,7 +303,9 @@ class DeclarationName { } static int compare(DeclarationName LHS, DeclarationName RHS); - + + void print(raw_ostream &OS, const PrintingPolicy &Policy); + void dump() const; }; diff --git a/lib/AST/DeclarationName.cpp b/lib/AST/DeclarationName.cpp index b2f27275f49..1f5ad4647cd 100644 --- a/lib/AST/DeclarationName.cpp +++ b/lib/AST/DeclarationName.cpp @@ -133,36 +133,43 @@ int DeclarationName::compare(DeclarationName LHS, DeclarationName RHS) { llvm_unreachable("Invalid DeclarationName Kind!"); } -raw_ostream &operator<<(raw_ostream &OS, DeclarationName N) { +static void printCXXConstructorDestructorName(QualType ClassType, + raw_ostream &OS, + const PrintingPolicy &Policy) { + if (const RecordType *ClassRec = ClassType->getAs()) { + OS << *ClassRec->getDecl(); + return; + } + if (!Policy.LangOpts.CPlusPlus) { + // Passed policy is the default one from operator <<, use a C++ policy. + LangOptions LO; + LO.CPlusPlus = true; + ClassType.print(OS, PrintingPolicy(LO)); + } else { + ClassType.print(OS, Policy); + } +} + +void DeclarationName::print(raw_ostream &OS, const PrintingPolicy &Policy) { + DeclarationName &N = *this; switch (N.getNameKind()) { case DeclarationName::Identifier: if (const IdentifierInfo *II = N.getAsIdentifierInfo()) OS << II->getName(); - return OS; + return; case DeclarationName::ObjCZeroArgSelector: case DeclarationName::ObjCOneArgSelector: case DeclarationName::ObjCMultiArgSelector: N.getObjCSelector().print(OS); - return OS; + return; - case DeclarationName::CXXConstructorName: { - QualType ClassType = N.getCXXNameType(); - if (const RecordType *ClassRec = ClassType->getAs()) - return OS << *ClassRec->getDecl(); - LangOptions LO; - LO.CPlusPlus = true; - return OS << ClassType.getAsString(PrintingPolicy(LO)); - } + case DeclarationName::CXXConstructorName: + return printCXXConstructorDestructorName(N.getCXXNameType(), OS, Policy); case DeclarationName::CXXDestructorName: { OS << '~'; - QualType Type = N.getCXXNameType(); - if (const RecordType *Rec = Type->getAs()) - return OS << *Rec->getDecl(); - LangOptions LO; - LO.CPlusPlus = true; - return OS << Type.getAsString(PrintingPolicy(LO)); + return printCXXConstructorDestructorName(N.getCXXNameType(), OS, Policy); } case DeclarationName::CXXOperatorName: { @@ -178,29 +185,46 @@ raw_ostream &operator<<(raw_ostream &OS, DeclarationName N) { OS << "operator"; if (OpName[0] >= 'a' && OpName[0] <= 'z') OS << ' '; - return OS << OpName; + OS << OpName; + return; } case DeclarationName::CXXLiteralOperatorName: - return OS << "operator\"\"" << N.getCXXLiteralIdentifier()->getName(); + OS << "operator\"\"" << N.getCXXLiteralIdentifier()->getName(); + return; case DeclarationName::CXXConversionFunctionName: { OS << "operator "; QualType Type = N.getCXXNameType(); - if (const RecordType *Rec = Type->getAs()) - return OS << *Rec->getDecl(); - LangOptions LO; - LO.CPlusPlus = true; - LO.Bool = true; - return OS << Type.getAsString(PrintingPolicy(LO)); + if (const RecordType *Rec = Type->getAs()) { + OS << *Rec->getDecl(); + return; + } + if (!Policy.LangOpts.CPlusPlus) { + // Passed policy is the default one from operator <<, use a C++ policy. + LangOptions LO; + LO.CPlusPlus = true; + LO.Bool = true; + Type.print(OS, PrintingPolicy(LO)); + } else { + Type.print(OS, Policy); + } + return; } case DeclarationName::CXXUsingDirective: - return OS << ""; + OS << ""; + return; } llvm_unreachable("Unexpected declaration name kind"); } +raw_ostream &operator<<(raw_ostream &OS, DeclarationName N) { + LangOptions LO; + N.print(OS, PrintingPolicy(LO)); + return OS; +} + } // end namespace clang DeclarationName::NameKind DeclarationName::getNameKind() const { From 93d151532ba2ec2f38147b372dd62bf3666238fa Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Sun, 14 Feb 2016 06:39:03 +0000 Subject: [PATCH 356/742] [index] Allow calling createIndexingAction() without passing another action to wrap over. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260841 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Index/IndexingAction.h | 7 +-- lib/Index/IndexingAction.cpp | 78 ++++++++++++++++++++-------- tools/libclang/Indexing.cpp | 5 +- 3 files changed, 63 insertions(+), 27 deletions(-) diff --git a/include/clang/Index/IndexingAction.h b/include/clang/Index/IndexingAction.h index dfc363a049d..3bb427e36d8 100644 --- a/include/clang/Index/IndexingAction.h +++ b/include/clang/Index/IndexingAction.h @@ -32,10 +32,11 @@ struct IndexingOptions { bool IndexFunctionLocals = false; }; +/// \param WrappedAction another frontend action to wrap over or null. std::unique_ptr -createIndexingAction(std::unique_ptr WrappedAction, - std::shared_ptr DataConsumer, - IndexingOptions Opts); +createIndexingAction(std::shared_ptr DataConsumer, + IndexingOptions Opts, + std::unique_ptr WrappedAction = nullptr); void indexASTUnit(ASTUnit &Unit, std::shared_ptr DataConsumer, diff --git a/lib/Index/IndexingAction.cpp b/lib/Index/IndexingAction.cpp index 3f7ef43e7dc..46c96a35b6f 100644 --- a/lib/Index/IndexingAction.cpp +++ b/lib/Index/IndexingAction.cpp @@ -68,18 +68,52 @@ class IndexASTConsumer : public ASTConsumer { } }; -class IndexAction : public WrapperFrontendAction { - IndexingOptions IndexOpts; +class IndexActionBase { +protected: std::shared_ptr DataConsumer; - std::unique_ptr IndexCtx; + IndexingContext IndexCtx; + + IndexActionBase(std::shared_ptr dataConsumer, + IndexingOptions Opts) + : DataConsumer(std::move(dataConsumer)), + IndexCtx(Opts, *DataConsumer) {} + + std::unique_ptr createIndexASTConsumer() { + return llvm::make_unique(IndexCtx); + } + void finish() { + DataConsumer->finish(); + } +}; + +class IndexAction : public ASTFrontendAction, IndexActionBase { public: - IndexAction(std::unique_ptr WrappedAction, - std::shared_ptr DataConsumer, + IndexAction(std::shared_ptr DataConsumer, IndexingOptions Opts) + : IndexActionBase(std::move(DataConsumer), Opts) {} + +protected: + std::unique_ptr CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override { + return createIndexASTConsumer(); + } + + void EndSourceFileAction() override { + FrontendAction::EndSourceFileAction(); + finish(); + } +}; + +class WrappingIndexAction : public WrapperFrontendAction, IndexActionBase { + bool IndexActionFailed = false; + +public: + WrappingIndexAction(std::unique_ptr WrappedAction, + std::shared_ptr DataConsumer, + IndexingOptions Opts) : WrapperFrontendAction(std::move(WrappedAction)), - IndexOpts(Opts), - DataConsumer(std::move(DataConsumer)) {} + IndexActionBase(std::move(DataConsumer), Opts) {} protected: std::unique_ptr CreateASTConsumer(CompilerInstance &CI, @@ -89,36 +123,36 @@ class IndexAction : public WrapperFrontendAction { } // anonymous namespace -void IndexAction::EndSourceFileAction() { +void WrappingIndexAction::EndSourceFileAction() { // Invoke wrapped action's method. WrapperFrontendAction::EndSourceFileAction(); - - bool IndexActionFailed = !IndexCtx; if (!IndexActionFailed) - DataConsumer->finish(); + finish(); } std::unique_ptr -IndexAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { +WrappingIndexAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { auto OtherConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile); - if (!OtherConsumer) + if (!OtherConsumer) { + IndexActionFailed = true; return nullptr; - - IndexCtx.reset(new IndexingContext(IndexOpts, *DataConsumer)); + } std::vector> Consumers; Consumers.push_back(std::move(OtherConsumer)); - Consumers.push_back(llvm::make_unique(*IndexCtx)); + Consumers.push_back(createIndexASTConsumer()); return llvm::make_unique(std::move(Consumers)); } std::unique_ptr -index::createIndexingAction(std::unique_ptr WrappedAction, - std::shared_ptr DataConsumer, - IndexingOptions Opts) { - return llvm::make_unique(std::move(WrappedAction), - std::move(DataConsumer), - Opts); +index::createIndexingAction(std::shared_ptr DataConsumer, + IndexingOptions Opts, + std::unique_ptr WrappedAction) { + if (WrappedAction) + return llvm::make_unique(std::move(WrappedAction), + std::move(DataConsumer), + Opts); + return llvm::make_unique(std::move(DataConsumer), Opts); } diff --git a/tools/libclang/Indexing.cpp b/tools/libclang/Indexing.cpp index b37a8864dd6..7b2bcc7aac2 100644 --- a/tools/libclang/Indexing.cpp +++ b/tools/libclang/Indexing.cpp @@ -564,8 +564,9 @@ static CXErrorCode clang_indexSourceFile_Impl( auto InterAction = llvm::make_unique(DataConsumer, SkipBodies ? IdxSession->SkipBodyData.get() : nullptr); std::unique_ptr IndexAction; - IndexAction = createIndexingAction(std::move(InterAction), DataConsumer, - getIndexingOptionsFromCXOptions(index_options)); + IndexAction = createIndexingAction(DataConsumer, + getIndexingOptionsFromCXOptions(index_options), + std::move(InterAction)); // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar From cc9d62186ce6b7ec75b95c26fb1a373cced8ebfd Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Sun, 14 Feb 2016 06:39:11 +0000 Subject: [PATCH 357/742] [index] Enhance c-index-test tool and have it link and test the clangIndex library directly. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260842 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Index/IndexSymbol.h | 8 ++ lib/Index/IndexSymbol.cpp | 99 +++++++++++++++ test/Index/Core/index-source.m | 8 ++ tools/c-index-test/CMakeLists.txt | 7 ++ tools/c-index-test/c-index-test.c | 20 ++- tools/c-index-test/core_main.cpp | 197 ++++++++++++++++++++++++++++++ 6 files changed, 335 insertions(+), 4 deletions(-) create mode 100644 test/Index/Core/index-source.m create mode 100644 tools/c-index-test/core_main.cpp diff --git a/include/clang/Index/IndexSymbol.h b/include/clang/Index/IndexSymbol.h index 7a505eac415..feee13cdbf5 100644 --- a/include/clang/Index/IndexSymbol.h +++ b/include/clang/Index/IndexSymbol.h @@ -11,6 +11,7 @@ #define LLVM_CLANG_INDEX_INDEXSYMBOL_H #include "clang/Basic/LLVM.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/Support/DataTypes.h" namespace clang { @@ -107,6 +108,13 @@ struct SymbolInfo { SymbolInfo getSymbolInfo(const Decl *D); +void applyForEachSymbolRole(SymbolRoleSet Roles, + llvm::function_ref Fn); +void printSymbolRoles(SymbolRoleSet Roles, raw_ostream &OS); +StringRef getSymbolKindString(SymbolKind K); +StringRef getTemplateKindStr(SymbolCXXTemplateKind TK); +StringRef getSymbolLanguageString(SymbolLanguage K); + } // namespace index } // namespace clang diff --git a/lib/Index/IndexSymbol.cpp b/lib/Index/IndexSymbol.cpp index c7c3c8c4d64..bf3bfd1b95b 100644 --- a/lib/Index/IndexSymbol.cpp +++ b/lib/Index/IndexSymbol.cpp @@ -185,3 +185,102 @@ SymbolInfo index::getSymbolInfo(const Decl *D) { return Info; } + +void index::applyForEachSymbolRole(SymbolRoleSet Roles, + llvm::function_ref Fn) { +#define APPLY_FOR_ROLE(Role) \ + if (Roles & (unsigned)SymbolRole::Role) \ + Fn(SymbolRole::Role) + + APPLY_FOR_ROLE(Declaration); + APPLY_FOR_ROLE(Definition); + APPLY_FOR_ROLE(Reference); + APPLY_FOR_ROLE(Read); + APPLY_FOR_ROLE(Write); + APPLY_FOR_ROLE(Call); + APPLY_FOR_ROLE(Dynamic); + APPLY_FOR_ROLE(AddressOf); + APPLY_FOR_ROLE(Implicit); + APPLY_FOR_ROLE(RelationChildOf); + APPLY_FOR_ROLE(RelationBaseOf); + APPLY_FOR_ROLE(RelationOverrideOf); + APPLY_FOR_ROLE(RelationReceivedBy); + +#undef APPLY_FOR_ROLE +} + +void index::printSymbolRoles(SymbolRoleSet Roles, raw_ostream &OS) { + bool VisitedOnce = false; + applyForEachSymbolRole(Roles, [&](SymbolRole Role) { + if (VisitedOnce) + OS << '/'; + else + VisitedOnce = true; + switch (Role) { + case SymbolRole::Declaration: OS << "Decl"; break; + case SymbolRole::Definition: OS << "Def"; break; + case SymbolRole::Reference: OS << "Ref"; break; + case SymbolRole::Read: OS << "Read"; break; + case SymbolRole::Write: OS << "Writ"; break; + case SymbolRole::Call: OS << "Call"; break; + case SymbolRole::Dynamic: OS << "Dyn"; break; + case SymbolRole::AddressOf: OS << "Addr"; break; + case SymbolRole::Implicit: OS << "Impl"; break; + case SymbolRole::RelationChildOf: OS << "RelChild"; break; + case SymbolRole::RelationBaseOf: OS << "RelBase"; break; + case SymbolRole::RelationOverrideOf: OS << "RelOver"; break; + case SymbolRole::RelationReceivedBy: OS << "RelRec"; break; + } + }); +} + +StringRef index::getSymbolKindString(SymbolKind K) { + switch (K) { + case SymbolKind::Unknown: return ""; + case SymbolKind::Module: return "module"; + case SymbolKind::Macro: return "macro"; + case SymbolKind::Enum: return "enum"; + case SymbolKind::Struct: return "struct"; + case SymbolKind::Union: return "union"; + case SymbolKind::Typedef: return "typedef"; + case SymbolKind::Function: return "function"; + case SymbolKind::Variable: return "variable"; + case SymbolKind::Field: return "field"; + case SymbolKind::EnumConstant: return "enumerator"; + case SymbolKind::ObjCClass: return "objc-class"; + case SymbolKind::ObjCProtocol: return "objc-protocol"; + case SymbolKind::ObjCCategory: return "objc-category"; + case SymbolKind::ObjCInstanceMethod: return "objc-instance-method"; + case SymbolKind::ObjCClassMethod: return "objc-class-method"; + case SymbolKind::ObjCProperty: return "objc-property"; + case SymbolKind::ObjCIvar: return "objc-ivar"; + case SymbolKind::CXXClass: return "c++-class"; + case SymbolKind::CXXNamespace: return "namespace"; + case SymbolKind::CXXNamespaceAlias: return "namespace-alias"; + case SymbolKind::CXXStaticVariable: return "c++-static-var"; + case SymbolKind::CXXStaticMethod: return "c++-static-method"; + case SymbolKind::CXXInstanceMethod: return "c++-instance-method"; + case SymbolKind::CXXConstructor: return "constructor"; + case SymbolKind::CXXDestructor: return "destructor"; + case SymbolKind::CXXConversionFunction: return "coversion-func"; + case SymbolKind::CXXTypeAlias: return "type-alias"; + case SymbolKind::CXXInterface: return "c++-__interface"; + } +} + +StringRef index::getTemplateKindStr(SymbolCXXTemplateKind TK) { + switch (TK) { + case SymbolCXXTemplateKind::NonTemplate: return "NT"; + case SymbolCXXTemplateKind::Template : return "T"; + case SymbolCXXTemplateKind::TemplatePartialSpecialization : return "TPS"; + case SymbolCXXTemplateKind::TemplateSpecialization: return "TS"; + } +} + +StringRef index::getSymbolLanguageString(SymbolLanguage K) { + switch (K) { + case SymbolLanguage::C: return "C"; + case SymbolLanguage::ObjC: return "ObjC"; + case SymbolLanguage::CXX: return "C++"; + } +} diff --git a/test/Index/Core/index-source.m b/test/Index/Core/index-source.m new file mode 100644 index 00000000000..42c23505b1b --- /dev/null +++ b/test/Index/Core/index-source.m @@ -0,0 +1,8 @@ +// RUN: c-index-test core -print-source-symbols -- %s | FileCheck %s + +@interface Base +// CHECK: [[@LINE-1]]:12 | objc-class/ObjC | Base | c:objc(cs)Base | Decl | rel: 0 +-(void)meth; +// CHECK: [[@LINE-1]]:1 | objc-instance-method/ObjC | meth | c:objc(cs)Base(im)meth | Decl/Dyn/RelChild | rel: 1 +// CHECK-NEXT: RelChild | Base | c:objc(cs)Base +@end diff --git a/tools/c-index-test/CMakeLists.txt b/tools/c-index-test/CMakeLists.txt index c78a42ffe8e..1228a654864 100644 --- a/tools/c-index-test/CMakeLists.txt +++ b/tools/c-index-test/CMakeLists.txt @@ -1,5 +1,10 @@ +set(LLVM_LINK_COMPONENTS + support +) + add_clang_executable(c-index-test c-index-test.c + core_main.cpp ) if(NOT MSVC) @@ -12,10 +17,12 @@ endif() if (LLVM_BUILD_STATIC) target_link_libraries(c-index-test libclang_static + clangIndex ) else() target_link_libraries(c-index-test libclang + clangIndex ) endif() diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c index b2f9120baf9..a67afb7cfa8 100644 --- a/tools/c-index-test/c-index-test.c +++ b/tools/c-index-test/c-index-test.c @@ -23,6 +23,8 @@ # include #endif +extern int indextest_core_main(int argc, const char **argv); + /******************************************************************************/ /* Utility functions. */ /******************************************************************************/ @@ -4410,13 +4412,15 @@ int cindextest_main(int argc, const char **argv) { * size). */ typedef struct thread_info { + int (*main_func)(int argc, const char **argv); int argc; const char **argv; int result; } thread_info; void thread_runner(void *client_data_v) { thread_info *client_data = client_data_v; - client_data->result = cindextest_main(client_data->argc, client_data->argv); + client_data->result = client_data->main_func(client_data->argc, + client_data->argv); } static void flush_atexit(void) { @@ -4435,11 +4439,19 @@ int main(int argc, const char **argv) { LIBXML_TEST_VERSION #endif - if (getenv("CINDEXTEST_NOTHREADS")) - return cindextest_main(argc, argv); - + client_data.main_func = cindextest_main; client_data.argc = argc; client_data.argv = argv; + + if (argc > 1 && strcmp(argv[1], "core") == 0) { + client_data.main_func = indextest_core_main; + --client_data.argc; + ++client_data.argv; + } + + if (getenv("CINDEXTEST_NOTHREADS")) + return client_data.main_func(client_data.argc, client_data.argv); + clang_executeOnThread(thread_runner, &client_data, 0); return client_data.result; } diff --git a/tools/c-index-test/core_main.cpp b/tools/c-index-test/core_main.cpp new file mode 100644 index 00000000000..d2faf2d0692 --- /dev/null +++ b/tools/c-index-test/core_main.cpp @@ -0,0 +1,197 @@ +//===-- core_main.cpp - Core Index Tool testbed ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Index/IndexingAction.h" +#include "clang/Index/IndexDataConsumer.h" +#include "clang/Index/USRGeneration.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/PrettyStackTrace.h" + +using namespace clang; +using namespace clang::index; +using namespace llvm; + +extern "C" int indextest_core_main(int argc, const char **argv); + +namespace { + +enum class ActionType { + None, + PrintSourceSymbols, +}; + +namespace options { + +static cl::OptionCategory IndexTestCoreCategory("index-test-core options"); + +static cl::opt +Action(cl::desc("Action:"), cl::init(ActionType::None), + cl::values( + clEnumValN(ActionType::PrintSourceSymbols, + "print-source-symbols", "Print symbols from source"), + clEnumValEnd), + cl::cat(IndexTestCoreCategory)); + +static cl::extrahelp MoreHelp( + "\nAdd \"-- \" at the end to setup the compiler " + "invocation\n" +); + +} +} // anonymous namespace + +static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS); +static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx, + raw_ostream &OS); + +namespace { + +class PrintIndexDataConsumer : public IndexDataConsumer { + raw_ostream &OS; + +public: + PrintIndexDataConsumer(raw_ostream &OS) : OS(OS) { + } + + bool handleDeclOccurence(const Decl *D, SymbolRoleSet Roles, + ArrayRef Relations, + FileID FID, unsigned Offset, + ASTNodeInfo ASTNode) override { + ASTContext &Ctx = D->getASTContext(); + SourceManager &SM = Ctx.getSourceManager(); + + unsigned Line = SM.getLineNumber(FID, Offset); + unsigned Col = SM.getColumnNumber(FID, Offset); + OS << Line << ':' << Col << " | "; + + printSymbolInfo(getSymbolInfo(D), OS); + OS << " | "; + + printSymbolNameAndUSR(D, Ctx, OS); + OS << " | "; + + printSymbolRoles(Roles, OS); + OS << " | "; + + OS << "rel: " << Relations.size() << '\n'; + + for (auto &SymRel : Relations) { + OS << '\t'; + printSymbolRoles(SymRel.Roles, OS); + OS << " | "; + printSymbolNameAndUSR(SymRel.RelatedSymbol, Ctx, OS); + OS << '\n'; + } + + return true; + } +}; + +} // anonymous namespace + +//===----------------------------------------------------------------------===// +// Print Source Symbols +//===----------------------------------------------------------------------===// + +static bool printSourceSymbols(ArrayRef Args) { + SmallVector ArgsWithProgName; + ArgsWithProgName.push_back("clang"); + ArgsWithProgName.append(Args.begin(), Args.end()); + IntrusiveRefCntPtr + Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions)); + IntrusiveRefCntPtr + CInvok(createInvocationFromCommandLine(ArgsWithProgName, Diags)); + if (!CInvok) + return true; + + auto DataConsumer = std::make_shared(outs()); + IndexingOptions IndexOpts; + std::unique_ptr IndexAction; + IndexAction = createIndexingAction(DataConsumer, IndexOpts); + + auto PCHContainerOps = std::make_shared(); + ASTUnit *Unit = + ASTUnit::LoadFromCompilerInvocationAction(CInvok.get(), PCHContainerOps, + Diags, IndexAction.get()); + + if (!Unit) + return true; + + return false; +} + +//===----------------------------------------------------------------------===// +// Helper Utils +//===----------------------------------------------------------------------===// + +static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS) { + OS << getSymbolKindString(SymInfo.Kind); + if (SymInfo.TemplateKind != SymbolCXXTemplateKind::NonTemplate) { + OS << '-' << getTemplateKindStr(SymInfo.TemplateKind); + } + OS << '/' << getSymbolLanguageString(SymInfo.Lang); +} + +static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx, + raw_ostream &OS) { + if (auto *ND = dyn_cast(D)) { + PrintingPolicy PrintPolicy(Ctx.getLangOpts()); + ND->getDeclName().print(OS, PrintPolicy); + } else { + OS << ""; + } + OS << " | "; + + SmallString<256> USRBuf; + if (generateUSRForDecl(D, USRBuf)) { + OS << ""; + } else { + OS << USRBuf; + } +} + +//===----------------------------------------------------------------------===// +// Command line processing. +//===----------------------------------------------------------------------===// + +int indextest_core_main(int argc, const char **argv) { + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + + std::vector CompArgs; + const char *const *DoubleDash = std::find(argv, argv + argc, StringRef("--")); + if (DoubleDash != argv + argc) { + CompArgs = std::vector(DoubleDash + 1, argv + argc); + argc = DoubleDash - argv; + } + + cl::HideUnrelatedOptions(options::IndexTestCoreCategory); + cl::ParseCommandLineOptions(argc, argv, "index-test-core"); + + if (options::Action == ActionType::None) { + errs() << "error: action required; pass '-help' for options\n"; + return 1; + } + + if (options::Action == ActionType::PrintSourceSymbols) { + if (CompArgs.empty()) { + errs() << "error: missing compiler args; pass '-- '\n"; + return 1; + } + return printSourceSymbols(CompArgs); + } + + return 0; +} From 1609dab8bc28111567173c3efc0974c6f5bcea0c Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Sun, 14 Feb 2016 06:53:20 +0000 Subject: [PATCH 358/742] [index] Fix gcc builds. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260843 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Index/IndexingAction.h | 2 +- tools/c-index-test/core_main.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/clang/Index/IndexingAction.h b/include/clang/Index/IndexingAction.h index 3bb427e36d8..e2e63dc6357 100644 --- a/include/clang/Index/IndexingAction.h +++ b/include/clang/Index/IndexingAction.h @@ -36,7 +36,7 @@ struct IndexingOptions { std::unique_ptr createIndexingAction(std::shared_ptr DataConsumer, IndexingOptions Opts, - std::unique_ptr WrappedAction = nullptr); + std::unique_ptr WrappedAction); void indexASTUnit(ASTUnit &Unit, std::shared_ptr DataConsumer, diff --git a/tools/c-index-test/core_main.cpp b/tools/c-index-test/core_main.cpp index d2faf2d0692..00fbd0c2dad 100644 --- a/tools/c-index-test/core_main.cpp +++ b/tools/c-index-test/core_main.cpp @@ -119,7 +119,8 @@ static bool printSourceSymbols(ArrayRef Args) { auto DataConsumer = std::make_shared(outs()); IndexingOptions IndexOpts; std::unique_ptr IndexAction; - IndexAction = createIndexingAction(DataConsumer, IndexOpts); + IndexAction = createIndexingAction(DataConsumer, IndexOpts, + /*WrappedAction=*/nullptr); auto PCHContainerOps = std::make_shared(); ASTUnit *Unit = From 99809dfa3bc8213409d3903a0d17435feb6c2283 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Sun, 14 Feb 2016 07:08:31 +0000 Subject: [PATCH 359/742] [c-index-test] Fix a gcc build error. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260844 91177308-0d34-0410-b5e6-96231b3b80d8 --- tools/c-index-test/core_main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/c-index-test/core_main.cpp b/tools/c-index-test/core_main.cpp index 00fbd0c2dad..ce37b562561 100644 --- a/tools/c-index-test/core_main.cpp +++ b/tools/c-index-test/core_main.cpp @@ -172,7 +172,7 @@ int indextest_core_main(int argc, const char **argv) { PrettyStackTraceProgram X(argc, argv); std::vector CompArgs; - const char *const *DoubleDash = std::find(argv, argv + argc, StringRef("--")); + const char **DoubleDash = std::find(argv, argv + argc, StringRef("--")); if (DoubleDash != argv + argc) { CompArgs = std::vector(DoubleDash + 1, argv + argc); argc = DoubleDash - argv; From c1a98c604622a46f3a9c8b8241b788023c9f7b68 Mon Sep 17 00:00:00 2001 From: NAKAMURA Takumi Date: Sun, 14 Feb 2016 09:19:04 +0000 Subject: [PATCH 360/742] c-index-test: Fix libdeps corresponding to r260841. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260847 91177308-0d34-0410-b5e6-96231b3b80d8 --- tools/c-index-test/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/c-index-test/CMakeLists.txt b/tools/c-index-test/CMakeLists.txt index 1228a654864..3958e95a90f 100644 --- a/tools/c-index-test/CMakeLists.txt +++ b/tools/c-index-test/CMakeLists.txt @@ -22,6 +22,9 @@ if (LLVM_BUILD_STATIC) else() target_link_libraries(c-index-test libclang + clangAST + clangBasic + clangFrontend clangIndex ) endif() From 2523b5da46ac65c60ee598fea0fb85ab02503d37 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Sat, 6 Feb 2016 22:36:34 +0000 Subject: [PATCH 361/742] Index: provide adjustment thunk information for C++ manglings Add support for exposing the adjustment thunk for virtual methods as appropriate. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260011 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/Index/print-cxx-manglings.cpp | 30 +++++++++++++++++++++++ tools/libclang/CIndex.cpp | 39 ++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/test/Index/print-cxx-manglings.cpp b/test/Index/print-cxx-manglings.cpp index aae29930172..27d7988eb79 100644 --- a/test/Index/print-cxx-manglings.cpp +++ b/test/Index/print-cxx-manglings.cpp @@ -64,3 +64,33 @@ struct v { // MSVC: CXXConstructor=v{{.*}}[mangled=??0v@@QAE@H@Z] [mangled=??_Fv@@QAEXXZ] +struct w { + virtual int m(int); +}; + +// ITANIUM: CXXMethod=m{{.*}} (virtual) [mangled=_ZN1w1mEi] + +// MACHO: CXXMethod=m{{.*}} (virtual) [mangled=__ZN1w1mEi] + +// MSVC: CXXMethod=m{{.*}} (virtual) [mangled=?m@w@@UAEHH@Z] + +struct x { + virtual int m(int); +}; + +// ITANIUM: CXXMethod=m{{.*}} (virtual) [mangled=_ZN1x1mEi] + +// MACHO: CXXMethod=m{{.*}} (virtual) [mangled=__ZN1x1mEi] + +// MSVC: CXXMethod=m{{.*}} (virtual) [mangled=?m@x@@UAEHH@Z] + +struct y : w, x { + virtual int m(int); +}; + +// ITANIUM: CXXMethod=m{{.*}} (virtual) {{.*}} [mangled=_ZN1y1mEi] [mangled=_ZThn4_N1y1mEi] + +// MACHO: CXXMethod=m{{.*}} (virtual) {{.*}} [mangled=__ZN1y1mEi] [mangled=__ZThn4_N1y1mEi] + +// MSVC: CXXMethod=m{{.*}} (virtual) {{.*}} [mangled=?m@y@@UAEHH@Z] [mangled=?m@y@@W3AEHH@Z] + diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index 9963f328ff6..448784e054f 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -24,6 +24,7 @@ #include "clang/AST/Attr.h" #include "clang/AST/Mangle.h" #include "clang/AST/StmtVisitor.h" +#include "clang/AST/VTableBuilder.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/DiagnosticCategories.h" #include "clang/Basic/DiagnosticIDs.h" @@ -4341,6 +4342,38 @@ CXString clang_Cursor_getMangling(CXCursor C) { return cxstring::createDup(FinalBufOS.str()); } +static std::string getMangledName(std::unique_ptr &M, + std::unique_ptr &DL, + const NamedDecl *ND) { + std::string FrontendBuf; + llvm::raw_string_ostream FOS(FrontendBuf); + + M->mangleName(ND, FOS); + + std::string BackendBuf; + llvm::raw_string_ostream BOS(BackendBuf); + + llvm::Mangler::getNameWithPrefix(BOS, llvm::Twine(FOS.str()), *DL); + + return BOS.str(); +} + +static std::string getMangledThunk(std::unique_ptr &M, + std::unique_ptr &DL, + const CXXMethodDecl *MD, const ThunkInfo &T) { + std::string FrontendBuf; + llvm::raw_string_ostream FOS(FrontendBuf); + + M->mangleThunk(MD, T, FOS); + + std::string BackendBuf; + llvm::raw_string_ostream BOS(BackendBuf); + + llvm::Mangler::getNameWithPrefix(BOS, llvm::Twine(FOS.str()), *DL); + + return BOS.str(); +} + CXStringSet *clang_Cursor_getCXXManglings(CXCursor C) { if (clang_isInvalid(C.kind) || !clang_isDeclaration(C.kind)) return nullptr; @@ -4384,6 +4417,12 @@ CXStringSet *clang_Cursor_getCXXManglings(CXCursor C) { if (DD->isVirtual()) Manglings.emplace_back(getMangledStructor(M, DL, DD, Dtor_Deleting)); } + } else if (const auto *MD = dyn_cast_or_null(ND)) { + Manglings.emplace_back(getMangledName(M, DL, ND)); + if (MD->isVirtual()) + if (const auto *TIV = Ctx.getVTableContext()->getThunkInfo(MD)) + for (const auto &T : *TIV) + Manglings.emplace_back(getMangledThunk(M, DL, MD, T)); } return cxstring::createSet(Manglings); From 7a5b05eb2ece0c3b63b2d13fe59f96a3a3d13ef8 Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Mon, 8 Feb 2016 15:52:13 +0000 Subject: [PATCH 362/742] Move static functions returning UDTs outside of the extern "C" block. Silences an MSVC warning, and reduces the number of exported symbols. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260104 91177308-0d34-0410-b5e6-96231b3b80d8 --- tools/libclang/CIndex.cpp | 65 ++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index 448784e054f..ad162f0366c 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -3969,6 +3969,39 @@ static std::string getMangledStructor(std::unique_ptr &M, return BOS.str(); } +static std::string getMangledName(std::unique_ptr &M, + std::unique_ptr &DL, + const NamedDecl *ND) { + std::string FrontendBuf; + llvm::raw_string_ostream FOS(FrontendBuf); + + M->mangleName(ND, FOS); + + std::string BackendBuf; + llvm::raw_string_ostream BOS(BackendBuf); + + llvm::Mangler::getNameWithPrefix(BOS, llvm::Twine(FOS.str()), *DL); + + return BOS.str(); +} + +static std::string getMangledThunk(std::unique_ptr &M, + std::unique_ptr &DL, + const CXXMethodDecl *MD, + const ThunkInfo &T) { + std::string FrontendBuf; + llvm::raw_string_ostream FOS(FrontendBuf); + + M->mangleThunk(MD, T, FOS); + + std::string BackendBuf; + llvm::raw_string_ostream BOS(BackendBuf); + + llvm::Mangler::getNameWithPrefix(BOS, llvm::Twine(FOS.str()), *DL); + + return BOS.str(); +} + extern "C" { unsigned clang_visitChildren(CXCursor parent, @@ -4342,38 +4375,6 @@ CXString clang_Cursor_getMangling(CXCursor C) { return cxstring::createDup(FinalBufOS.str()); } -static std::string getMangledName(std::unique_ptr &M, - std::unique_ptr &DL, - const NamedDecl *ND) { - std::string FrontendBuf; - llvm::raw_string_ostream FOS(FrontendBuf); - - M->mangleName(ND, FOS); - - std::string BackendBuf; - llvm::raw_string_ostream BOS(BackendBuf); - - llvm::Mangler::getNameWithPrefix(BOS, llvm::Twine(FOS.str()), *DL); - - return BOS.str(); -} - -static std::string getMangledThunk(std::unique_ptr &M, - std::unique_ptr &DL, - const CXXMethodDecl *MD, const ThunkInfo &T) { - std::string FrontendBuf; - llvm::raw_string_ostream FOS(FrontendBuf); - - M->mangleThunk(MD, T, FOS); - - std::string BackendBuf; - llvm::raw_string_ostream BOS(BackendBuf); - - llvm::Mangler::getNameWithPrefix(BOS, llvm::Twine(FOS.str()), *DL); - - return BOS.str(); -} - CXStringSet *clang_Cursor_getCXXManglings(CXCursor C) { if (clang_isInvalid(C.kind) || !clang_isDeclaration(C.kind)) return nullptr; From aea6d868dbbf9750f08dfe12091db8e3ed4bdd33 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Sun, 14 Feb 2016 22:30:14 +0000 Subject: [PATCH 363/742] [index] Factor libclang's functionality to determing the mangled name of symbols into the clangIndex library. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260858 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Mangle.h | 1 + include/clang/Index/CodegenNameGenerator.h | 52 ++++++ include/clang/Index/IndexDataConsumer.h | 3 + lib/AST/Mangle.cpp | 16 +- lib/Index/CMakeLists.txt | 3 +- lib/Index/CodegenNameGenerator.cpp | 197 +++++++++++++++++++++ lib/Index/IndexingAction.cpp | 2 + test/Index/Core/index-source.m | 4 +- tools/c-index-test/core_main.cpp | 10 ++ tools/libclang/CIndex.cpp | 131 +------------- 10 files changed, 286 insertions(+), 133 deletions(-) create mode 100644 include/clang/Index/CodegenNameGenerator.h create mode 100644 lib/Index/CodegenNameGenerator.cpp diff --git a/include/clang/AST/Mangle.h b/include/clang/AST/Mangle.h index 4872738e7a1..dad9269f842 100644 --- a/include/clang/AST/Mangle.h +++ b/include/clang/AST/Mangle.h @@ -123,6 +123,7 @@ class MangleContext { void mangleBlock(const DeclContext *DC, const BlockDecl *BD, raw_ostream &Out); + void mangleObjCMethodNameWithoutSize(const ObjCMethodDecl *MD, raw_ostream &); void mangleObjCMethodName(const ObjCMethodDecl *MD, raw_ostream &); virtual void mangleStaticGuardVariable(const VarDecl *D, raw_ostream &) = 0; diff --git a/include/clang/Index/CodegenNameGenerator.h b/include/clang/Index/CodegenNameGenerator.h new file mode 100644 index 00000000000..e8dc196a204 --- /dev/null +++ b/include/clang/Index/CodegenNameGenerator.h @@ -0,0 +1,52 @@ +//===- CodegenNameGenerator.h - Codegen name generation -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Determines the name that the symbol will get for code generation. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INDEX_CODEGENNAMEGENERATOR_H +#define LLVM_CLANG_INDEX_CODEGENNAMEGENERATOR_H + +#include "clang/Basic/LLVM.h" +#include +#include +#include + +namespace clang { + class ASTContext; + class Decl; + +namespace index { + +class CodegenNameGenerator { +public: + explicit CodegenNameGenerator(ASTContext &Ctx); + ~CodegenNameGenerator(); + + /// \returns true on failure to produce a name for the given decl, false on + /// success. + bool writeName(const Decl *D, raw_ostream &OS); + + /// Version of \c writeName function that returns a string. + std::string getName(const Decl *D); + + /// This can return multiple mangled names when applicable, e.g. for C++ + /// constructors/destructors. + std::vector getAllManglings(const Decl *D); + +private: + struct Implementation; + std::unique_ptr Impl; +}; + +} // namespace index +} // namespace clang + +#endif // LLVM_CLANG_INDEX_CODEGENNAMEGENERATOR_H diff --git a/include/clang/Index/IndexDataConsumer.h b/include/clang/Index/IndexDataConsumer.h index cb00345f56b..31543704834 100644 --- a/include/clang/Index/IndexDataConsumer.h +++ b/include/clang/Index/IndexDataConsumer.h @@ -13,6 +13,7 @@ #include "clang/Index/IndexSymbol.h" namespace clang { + class ASTContext; class DeclContext; class Expr; class FileID; @@ -33,6 +34,8 @@ class IndexDataConsumer { virtual ~IndexDataConsumer() {} + virtual void initialize(ASTContext &Ctx) {} + /// \returns true to continue indexing, or false to abort. virtual bool handleDeclOccurence(const Decl *D, SymbolRoleSet Roles, ArrayRef Relations, diff --git a/lib/AST/Mangle.cpp b/lib/AST/Mangle.cpp index b1193ca2d23..7a9cf18acc1 100644 --- a/lib/AST/Mangle.cpp +++ b/lib/AST/Mangle.cpp @@ -254,11 +254,8 @@ void MangleContext::mangleBlock(const DeclContext *DC, const BlockDecl *BD, mangleFunctionBlock(*this, Buffer, BD, Out); } -void MangleContext::mangleObjCMethodName(const ObjCMethodDecl *MD, - raw_ostream &Out) { - SmallString<64> Name; - llvm::raw_svector_ostream OS(Name); - +void MangleContext::mangleObjCMethodNameWithoutSize(const ObjCMethodDecl *MD, + raw_ostream &OS) { const ObjCContainerDecl *CD = dyn_cast(MD->getDeclContext()); assert (CD && "Missing container decl in GetNameForMethod"); @@ -268,6 +265,13 @@ void MangleContext::mangleObjCMethodName(const ObjCMethodDecl *MD, OS << ' '; MD->getSelector().print(OS); OS << ']'; - +} + +void MangleContext::mangleObjCMethodName(const ObjCMethodDecl *MD, + raw_ostream &Out) { + SmallString<64> Name; + llvm::raw_svector_ostream OS(Name); + + mangleObjCMethodNameWithoutSize(MD, OS); Out << OS.str().size() << OS.str(); } diff --git a/lib/Index/CMakeLists.txt b/lib/Index/CMakeLists.txt index cb2b931235e..7e39e498c8f 100644 --- a/lib/Index/CMakeLists.txt +++ b/lib/Index/CMakeLists.txt @@ -3,14 +3,15 @@ set(LLVM_LINK_COMPONENTS ) add_clang_library(clangIndex + CodegenNameGenerator.cpp CommentToXML.cpp - USRGeneration.cpp IndexBody.cpp IndexDecl.cpp IndexingAction.cpp IndexingContext.cpp IndexSymbol.cpp IndexTypeSourceInfo.cpp + USRGeneration.cpp ADDITIONAL_HEADERS IndexingContext.h diff --git a/lib/Index/CodegenNameGenerator.cpp b/lib/Index/CodegenNameGenerator.cpp new file mode 100644 index 00000000000..d663cc36b70 --- /dev/null +++ b/lib/Index/CodegenNameGenerator.cpp @@ -0,0 +1,197 @@ +//===- CodegenNameGenerator.cpp - Codegen name generation -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Determines the name that the symbol will get for code generation. +// +//===----------------------------------------------------------------------===// + +#include "clang/Index/CodegenNameGenerator.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Mangle.h" +#include "clang/AST/VTableBuilder.h" +#include "clang/Basic/TargetInfo.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/Mangler.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::index; + +struct CodegenNameGenerator::Implementation { + std::unique_ptr MC; + llvm::DataLayout DL; + + Implementation(ASTContext &Ctx) + : MC(Ctx.createMangleContext()), + DL(Ctx.getTargetInfo().getDataLayoutString()) {} + + bool writeName(const Decl *D, raw_ostream &OS) { + // First apply frontend mangling. + SmallString<128> FrontendBuf; + llvm::raw_svector_ostream FrontendBufOS(FrontendBuf); + if (auto *FD = dyn_cast(D)) { + if (FD->isDependentContext()) + return true; + if (writeFuncOrVarName(FD, FrontendBufOS)) + return true; + } else if (auto *VD = dyn_cast(D)) { + if (writeFuncOrVarName(VD, FrontendBufOS)) + return true; + } else if (auto *MD = dyn_cast(D)) { + MC->mangleObjCMethodNameWithoutSize(MD, OS); + return false; + } else if (auto *ID = dyn_cast(D)) { + writeObjCClassName(ID, FrontendBufOS); + } else { + return true; + } + + // Now apply backend mangling. + llvm::Mangler::getNameWithPrefix(OS, FrontendBufOS.str(), DL); + return false; + } + + std::string getName(const Decl *D) { + std::string Name; + { + llvm::raw_string_ostream OS(Name); + writeName(D, OS); + } + return Name; + } + + std::vector getAllManglings(const Decl *D) { + if (!(isa(D) || isa(D))) + return {}; + + const NamedDecl *ND = cast(D); + + ASTContext &Ctx = ND->getASTContext(); + std::unique_ptr M(Ctx.createMangleContext()); + std::unique_ptr DL( + new llvm::DataLayout(Ctx.getTargetInfo().getDataLayoutString())); + + std::vector Manglings; + + auto hasDefaultCXXMethodCC = [](ASTContext &C, const CXXMethodDecl *MD) { + auto DefaultCC = C.getDefaultCallingConvention(/*IsVariadic=*/false, + /*IsCSSMethod=*/true); + auto CC = MD->getType()->getAs()->getCallConv(); + return CC == DefaultCC; + }; + + if (const auto *CD = dyn_cast_or_null(ND)) { + Manglings.emplace_back(getMangledStructor(CD, Ctor_Base)); + + if (Ctx.getTargetInfo().getCXXABI().isItaniumFamily()) + if (!CD->getParent()->isAbstract()) + Manglings.emplace_back(getMangledStructor(CD, Ctor_Complete)); + + if (Ctx.getTargetInfo().getCXXABI().isMicrosoft()) + if (CD->hasAttr() && CD->isDefaultConstructor()) + if (!(hasDefaultCXXMethodCC(Ctx, CD) && CD->getNumParams() == 0)) + Manglings.emplace_back(getMangledStructor(CD, Ctor_DefaultClosure)); + } else if (const auto *DD = dyn_cast_or_null(ND)) { + Manglings.emplace_back(getMangledStructor(DD, Dtor_Base)); + if (Ctx.getTargetInfo().getCXXABI().isItaniumFamily()) { + Manglings.emplace_back(getMangledStructor(DD, Dtor_Complete)); + if (DD->isVirtual()) + Manglings.emplace_back(getMangledStructor(DD, Dtor_Deleting)); + } + } else if (const auto *MD = dyn_cast_or_null(ND)) { + Manglings.emplace_back(getName(ND)); + if (MD->isVirtual()) + if (const auto *TIV = Ctx.getVTableContext()->getThunkInfo(MD)) + for (const auto &T : *TIV) + Manglings.emplace_back(getMangledThunk(MD, T)); + } + + return Manglings; + } + +private: + bool writeFuncOrVarName(const NamedDecl *D, raw_ostream &OS) { + if (MC->shouldMangleDeclName(D)) { + if (const auto *CtorD = dyn_cast(D)) + MC->mangleCXXCtor(CtorD, Ctor_Complete, OS); + else if (const auto *DtorD = dyn_cast(D)) + MC->mangleCXXDtor(DtorD, Dtor_Complete, OS); + else + MC->mangleName(D, OS); + return false; + } else { + IdentifierInfo *II = D->getIdentifier(); + if (!II) + return true; + OS << II->getName(); + return false; + } + } + + void writeObjCClassName(const ObjCInterfaceDecl *D, raw_ostream &OS) { + OS << getClassSymbolPrefix(); + OS << D->getObjCRuntimeNameAsString(); + } + + static StringRef getClassSymbolPrefix() { + return "OBJC_CLASS_$_"; + } + + std::string getMangledStructor(const NamedDecl *ND, unsigned StructorType) { + std::string FrontendBuf; + llvm::raw_string_ostream FOS(FrontendBuf); + + if (const auto *CD = dyn_cast_or_null(ND)) + MC->mangleCXXCtor(CD, static_cast(StructorType), FOS); + else if (const auto *DD = dyn_cast_or_null(ND)) + MC->mangleCXXDtor(DD, static_cast(StructorType), FOS); + + std::string BackendBuf; + llvm::raw_string_ostream BOS(BackendBuf); + + llvm::Mangler::getNameWithPrefix(BOS, FOS.str(), DL); + + return BOS.str(); + } + + std::string getMangledThunk(const CXXMethodDecl *MD, const ThunkInfo &T) { + std::string FrontendBuf; + llvm::raw_string_ostream FOS(FrontendBuf); + + MC->mangleThunk(MD, T, FOS); + + std::string BackendBuf; + llvm::raw_string_ostream BOS(BackendBuf); + + llvm::Mangler::getNameWithPrefix(BOS, FOS.str(), DL); + + return BOS.str(); + } +}; + +CodegenNameGenerator::CodegenNameGenerator(ASTContext &Ctx) + : Impl(new Implementation(Ctx)) { +} + +CodegenNameGenerator::~CodegenNameGenerator() { +} + +bool CodegenNameGenerator::writeName(const Decl *D, raw_ostream &OS) { + return Impl->writeName(D, OS); +} + +std::string CodegenNameGenerator::getName(const Decl *D) { + return Impl->getName(D); +} + +std::vector CodegenNameGenerator::getAllManglings(const Decl *D) { + return Impl->getAllManglings(D); +} diff --git a/lib/Index/IndexingAction.cpp b/lib/Index/IndexingAction.cpp index 46c96a35b6f..d7442931523 100644 --- a/lib/Index/IndexingAction.cpp +++ b/lib/Index/IndexingAction.cpp @@ -50,6 +50,7 @@ class IndexASTConsumer : public ASTConsumer { protected: void Initialize(ASTContext &Context) override { IndexCtx.setASTContext(Context); + IndexCtx.getDataConsumer().initialize(Context); } bool HandleTopLevelDecl(DeclGroupRef DG) override { @@ -170,5 +171,6 @@ void index::indexASTUnit(ASTUnit &Unit, IndexingOptions Opts) { IndexingContext IndexCtx(Opts, *DataConsumer); IndexCtx.setASTContext(Unit.getASTContext()); + DataConsumer->initialize(Unit.getASTContext()); indexTranslationUnit(Unit, IndexCtx); } diff --git a/test/Index/Core/index-source.m b/test/Index/Core/index-source.m index 42c23505b1b..b5a86fbcb4a 100644 --- a/test/Index/Core/index-source.m +++ b/test/Index/Core/index-source.m @@ -1,8 +1,8 @@ // RUN: c-index-test core -print-source-symbols -- %s | FileCheck %s @interface Base -// CHECK: [[@LINE-1]]:12 | objc-class/ObjC | Base | c:objc(cs)Base | Decl | rel: 0 +// CHECK: [[@LINE-1]]:12 | objc-class/ObjC | Base | c:objc(cs)Base | _OBJC_CLASS_$_Base | Decl | rel: 0 -(void)meth; -// CHECK: [[@LINE-1]]:1 | objc-instance-method/ObjC | meth | c:objc(cs)Base(im)meth | Decl/Dyn/RelChild | rel: 1 +// CHECK: [[@LINE-1]]:1 | objc-instance-method/ObjC | meth | c:objc(cs)Base(im)meth | -[Base meth] | Decl/Dyn/RelChild | rel: 1 // CHECK-NEXT: RelChild | Base | c:objc(cs)Base @end diff --git a/tools/c-index-test/core_main.cpp b/tools/c-index-test/core_main.cpp index ce37b562561..b0d40edd4bb 100644 --- a/tools/c-index-test/core_main.cpp +++ b/tools/c-index-test/core_main.cpp @@ -14,6 +14,7 @@ #include "clang/Index/IndexingAction.h" #include "clang/Index/IndexDataConsumer.h" #include "clang/Index/USRGeneration.h" +#include "clang/Index/CodegenNameGenerator.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Signals.h" #include "llvm/Support/raw_ostream.h" @@ -60,11 +61,16 @@ namespace { class PrintIndexDataConsumer : public IndexDataConsumer { raw_ostream &OS; + std::unique_ptr CGNameGen; public: PrintIndexDataConsumer(raw_ostream &OS) : OS(OS) { } + void initialize(ASTContext &Ctx) override { + CGNameGen.reset(new CodegenNameGenerator(Ctx)); + } + bool handleDeclOccurence(const Decl *D, SymbolRoleSet Roles, ArrayRef Relations, FileID FID, unsigned Offset, @@ -82,6 +88,10 @@ class PrintIndexDataConsumer : public IndexDataConsumer { printSymbolNameAndUSR(D, Ctx, OS); OS << " | "; + if (CGNameGen->writeName(D, OS)) + OS << ""; + OS << " | "; + printSymbolRoles(Roles, OS); OS << " | "; diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index ad162f0366c..49ae3ae3b2d 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -22,17 +22,15 @@ #include "CXType.h" #include "CursorVisitor.h" #include "clang/AST/Attr.h" -#include "clang/AST/Mangle.h" #include "clang/AST/StmtVisitor.h" -#include "clang/AST/VTableBuilder.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/DiagnosticCategories.h" #include "clang/Basic/DiagnosticIDs.h" -#include "clang/Basic/TargetInfo.h" #include "clang/Basic/Version.h" #include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Index/CodegenNameGenerator.h" #include "clang/Index/CommentToXML.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Lexer.h" @@ -43,8 +41,6 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Config/llvm-config.h" -#include "llvm/IR/DataLayout.h" -#include "llvm/IR/Mangler.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/Format.h" @@ -3949,59 +3945,6 @@ static SourceLocation getLocationFromExpr(const Expr *E) { return E->getLocStart(); } -static std::string getMangledStructor(std::unique_ptr &M, - std::unique_ptr &DL, - const NamedDecl *ND, - unsigned StructorType) { - std::string FrontendBuf; - llvm::raw_string_ostream FOS(FrontendBuf); - - if (const auto *CD = dyn_cast_or_null(ND)) - M->mangleCXXCtor(CD, static_cast(StructorType), FOS); - else if (const auto *DD = dyn_cast_or_null(ND)) - M->mangleCXXDtor(DD, static_cast(StructorType), FOS); - - std::string BackendBuf; - llvm::raw_string_ostream BOS(BackendBuf); - - llvm::Mangler::getNameWithPrefix(BOS, llvm::Twine(FOS.str()), *DL); - - return BOS.str(); -} - -static std::string getMangledName(std::unique_ptr &M, - std::unique_ptr &DL, - const NamedDecl *ND) { - std::string FrontendBuf; - llvm::raw_string_ostream FOS(FrontendBuf); - - M->mangleName(ND, FOS); - - std::string BackendBuf; - llvm::raw_string_ostream BOS(BackendBuf); - - llvm::Mangler::getNameWithPrefix(BOS, llvm::Twine(FOS.str()), *DL); - - return BOS.str(); -} - -static std::string getMangledThunk(std::unique_ptr &M, - std::unique_ptr &DL, - const CXXMethodDecl *MD, - const ThunkInfo &T) { - std::string FrontendBuf; - llvm::raw_string_ostream FOS(FrontendBuf); - - M->mangleThunk(MD, T, FOS); - - std::string BackendBuf; - llvm::raw_string_ostream BOS(BackendBuf); - - llvm::Mangler::getNameWithPrefix(BOS, llvm::Twine(FOS.str()), *DL); - - return BOS.str(); -} - extern "C" { unsigned clang_visitChildren(CXCursor parent, @@ -4350,29 +4293,9 @@ CXString clang_Cursor_getMangling(CXCursor C) { if (!D || !(isa(D) || isa(D))) return cxstring::createEmpty(); - // First apply frontend mangling. - const NamedDecl *ND = cast(D); - ASTContext &Ctx = ND->getASTContext(); - std::unique_ptr MC(Ctx.createMangleContext()); - - std::string FrontendBuf; - llvm::raw_string_ostream FrontendBufOS(FrontendBuf); - if (MC->shouldMangleDeclName(ND)) { - MC->mangleName(ND, FrontendBufOS); - } else { - ND->printName(FrontendBufOS); - } - - // Now apply backend mangling. - std::unique_ptr DL( - new llvm::DataLayout(Ctx.getTargetInfo().getDataLayoutString())); - - std::string FinalBuf; - llvm::raw_string_ostream FinalBufOS(FinalBuf); - llvm::Mangler::getNameWithPrefix(FinalBufOS, llvm::Twine(FrontendBufOS.str()), - *DL); - - return cxstring::createDup(FinalBufOS.str()); + ASTContext &Ctx = D->getASTContext(); + index::CodegenNameGenerator CGNameGen(Ctx); + return cxstring::createDup(CGNameGen.getName(D)); } CXStringSet *clang_Cursor_getCXXManglings(CXCursor C) { @@ -4383,49 +4306,9 @@ CXStringSet *clang_Cursor_getCXXManglings(CXCursor C) { if (!(isa(D) || isa(D))) return nullptr; - const NamedDecl *ND = cast(D); - - ASTContext &Ctx = ND->getASTContext(); - std::unique_ptr M(Ctx.createMangleContext()); - std::unique_ptr DL( - new llvm::DataLayout(Ctx.getTargetInfo().getDataLayoutString())); - - std::vector Manglings; - - auto hasDefaultCXXMethodCC = [](ASTContext &C, const CXXMethodDecl *MD) { - auto DefaultCC = C.getDefaultCallingConvention(/*IsVariadic=*/false, - /*IsCSSMethod=*/true); - auto CC = MD->getType()->getAs()->getCallConv(); - return CC == DefaultCC; - }; - - if (const auto *CD = dyn_cast_or_null(ND)) { - Manglings.emplace_back(getMangledStructor(M, DL, CD, Ctor_Base)); - - if (Ctx.getTargetInfo().getCXXABI().isItaniumFamily()) - if (!CD->getParent()->isAbstract()) - Manglings.emplace_back(getMangledStructor(M, DL, CD, Ctor_Complete)); - - if (Ctx.getTargetInfo().getCXXABI().isMicrosoft()) - if (CD->hasAttr() && CD->isDefaultConstructor()) - if (!(hasDefaultCXXMethodCC(Ctx, CD) && CD->getNumParams() == 0)) - Manglings.emplace_back(getMangledStructor(M, DL, CD, - Ctor_DefaultClosure)); - } else if (const auto *DD = dyn_cast_or_null(ND)) { - Manglings.emplace_back(getMangledStructor(M, DL, DD, Dtor_Base)); - if (Ctx.getTargetInfo().getCXXABI().isItaniumFamily()) { - Manglings.emplace_back(getMangledStructor(M, DL, DD, Dtor_Complete)); - if (DD->isVirtual()) - Manglings.emplace_back(getMangledStructor(M, DL, DD, Dtor_Deleting)); - } - } else if (const auto *MD = dyn_cast_or_null(ND)) { - Manglings.emplace_back(getMangledName(M, DL, ND)); - if (MD->isVirtual()) - if (const auto *TIV = Ctx.getVTableContext()->getThunkInfo(MD)) - for (const auto &T : *TIV) - Manglings.emplace_back(getMangledThunk(M, DL, MD, T)); - } - + ASTContext &Ctx = D->getASTContext(); + index::CodegenNameGenerator CGNameGen(Ctx); + std::vector Manglings = CGNameGen.getAllManglings(D); return cxstring::createSet(Manglings); } From e5605f763b862c9ea42ddb081a27b184a2bec743 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Sun, 14 Feb 2016 22:38:38 +0000 Subject: [PATCH 364/742] [test/Index] Set a specific target for the test. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260861 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/Index/Core/index-source.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Index/Core/index-source.m b/test/Index/Core/index-source.m index b5a86fbcb4a..0fdae7ae5f9 100644 --- a/test/Index/Core/index-source.m +++ b/test/Index/Core/index-source.m @@ -1,4 +1,4 @@ -// RUN: c-index-test core -print-source-symbols -- %s | FileCheck %s +// RUN: c-index-test core -print-source-symbols -- %s -target x86_64-apple-macosx10.7 | FileCheck %s @interface Base // CHECK: [[@LINE-1]]:12 | objc-class/ObjC | Base | c:objc(cs)Base | _OBJC_CLASS_$_Base | Decl | rel: 0 From c6aaf1c3c6e33fedc1e7e0513094c95618100ae4 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Mon, 15 Feb 2016 01:32:36 +0000 Subject: [PATCH 365/742] [AST/index] Introduce an option 'SuppressTemplateArgsInCXXConstructors' in printing policy. Enable it for USRs and names when indexing. Forward references can have different template argument names; including them makes USRs and names unstable, since the name depends on whether we saw a forward reference or not. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260866 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/PrettyPrinter.h | 7 ++++++- include/clang/Index/IndexSymbol.h | 5 +++++ lib/AST/DeclarationName.cpp | 8 +++++++- lib/Index/IndexSymbol.cpp | 19 +++++++++++++++++++ lib/Index/USRGeneration.cpp | 7 ++++++- test/Index/Core/index-source.cpp | 9 +++++++++ tools/c-index-test/core_main.cpp | 5 +---- 7 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 test/Index/Core/index-source.cpp diff --git a/include/clang/AST/PrettyPrinter.h b/include/clang/AST/PrettyPrinter.h index 8ab3f61703a..57495efa966 100644 --- a/include/clang/AST/PrettyPrinter.h +++ b/include/clang/AST/PrettyPrinter.h @@ -40,6 +40,7 @@ struct PrintingPolicy { SuppressUnwrittenScope(false), SuppressInitializers(false), ConstantArraySizeAsWritten(false), AnonymousTagLocations(true), SuppressStrongLifetime(false), SuppressLifetimeQualifiers(false), + SuppressTemplateArgsInCXXConstructors(false), Bool(LO.Bool), TerseOutput(false), PolishForDeclaration(false), Half(LO.Half), MSWChar(LO.MicrosoftExt && !LO.WChar), IncludeNewlines(true), MSVCFormatting(false) { } @@ -136,7 +137,11 @@ struct PrintingPolicy { /// \brief When true, suppress printing of lifetime qualifier in /// ARC. unsigned SuppressLifetimeQualifiers : 1; - + + /// When true, suppresses printing template arguments in names of C++ + /// constructors. + unsigned SuppressTemplateArgsInCXXConstructors : 1; + /// \brief Whether we can use 'bool' rather than '_Bool', even if the language /// doesn't actually have 'bool' (because, e.g., it is defined as a macro). unsigned Bool : 1; diff --git a/include/clang/Index/IndexSymbol.h b/include/clang/Index/IndexSymbol.h index feee13cdbf5..506540b0e6e 100644 --- a/include/clang/Index/IndexSymbol.h +++ b/include/clang/Index/IndexSymbol.h @@ -16,6 +16,7 @@ namespace clang { class Decl; + class LangOptions; namespace index { @@ -111,6 +112,10 @@ SymbolInfo getSymbolInfo(const Decl *D); void applyForEachSymbolRole(SymbolRoleSet Roles, llvm::function_ref Fn); void printSymbolRoles(SymbolRoleSet Roles, raw_ostream &OS); + +/// \returns true if no name was printed, false otherwise. +bool printSymbolName(const Decl *D, const LangOptions &LO, raw_ostream &OS); + StringRef getSymbolKindString(SymbolKind K); StringRef getTemplateKindStr(SymbolCXXTemplateKind TK); StringRef getSymbolLanguageString(SymbolLanguage K); diff --git a/lib/AST/DeclarationName.cpp b/lib/AST/DeclarationName.cpp index 1f5ad4647cd..e8363572256 100644 --- a/lib/AST/DeclarationName.cpp +++ b/lib/AST/DeclarationName.cpp @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// #include "clang/AST/ASTContext.h" -#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/DeclarationName.h" #include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" @@ -140,6 +140,12 @@ static void printCXXConstructorDestructorName(QualType ClassType, OS << *ClassRec->getDecl(); return; } + if (Policy.SuppressTemplateArgsInCXXConstructors) { + if (auto *InjTy = ClassType->getAs()) { + OS << *InjTy->getDecl(); + return; + } + } if (!Policy.LangOpts.CPlusPlus) { // Passed policy is the default one from operator <<, use a C++ policy. LangOptions LO; diff --git a/lib/Index/IndexSymbol.cpp b/lib/Index/IndexSymbol.cpp index bf3bfd1b95b..0106cd6134c 100644 --- a/lib/Index/IndexSymbol.cpp +++ b/lib/Index/IndexSymbol.cpp @@ -11,6 +11,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" +#include "clang/AST/PrettyPrinter.h" using namespace clang; using namespace clang::index; @@ -234,6 +235,24 @@ void index::printSymbolRoles(SymbolRoleSet Roles, raw_ostream &OS) { }); } +bool index::printSymbolName(const Decl *D, const LangOptions &LO, + raw_ostream &OS) { + if (auto *ND = dyn_cast(D)) { + PrintingPolicy Policy(LO); + // Forward references can have different template argument names. Suppress + // the template argument names in constructors to make their name more + // stable. + Policy.SuppressTemplateArgsInCXXConstructors = true; + DeclarationName DeclName = ND->getDeclName(); + if (DeclName.isEmpty()) + return true; + DeclName.print(OS, Policy); + return false; + } else { + return true; + } +} + StringRef index::getSymbolKindString(SymbolKind K) { switch (K) { case SymbolKind::Unknown: return ""; diff --git a/lib/Index/USRGeneration.cpp b/lib/Index/USRGeneration.cpp index c57694fc10a..3dfc27df42f 100644 --- a/lib/Index/USRGeneration.cpp +++ b/lib/Index/USRGeneration.cpp @@ -203,7 +203,12 @@ void USRGenerator::VisitFunctionDecl(const FunctionDecl *D) { VisitTemplateParameterList(FunTmpl->getTemplateParameters()); } else Out << "@F@"; - D->printName(Out); + + PrintingPolicy Policy(Context->getLangOpts()); + // Forward references can have different template argument names. Suppress the + // template argument names in constructors to make their USR more stable. + Policy.SuppressTemplateArgsInCXXConstructors = true; + D->getDeclName().print(Out, Policy); ASTContext &Ctx = *Context; if (!Ctx.getLangOpts().CPlusPlus || D->isExternC()) diff --git a/test/Index/Core/index-source.cpp b/test/Index/Core/index-source.cpp new file mode 100644 index 00000000000..75446468266 --- /dev/null +++ b/test/Index/Core/index-source.cpp @@ -0,0 +1,9 @@ +// RUN: c-index-test core -print-source-symbols -- %s -target x86_64-apple-macosx10.7 | FileCheck %s + +template +class TemplCls { +// CHECK: [[@LINE-1]]:7 | c++-class/C++ | TemplCls | c:@ST>1#T@TemplCls | | Def | rel: 0 + TemplCls(int x); + // CHECK: [[@LINE-1]]:3 | constructor/C++ | TemplCls | c:@ST>1#T@TemplCls@F@TemplCls#I# | | Decl/RelChild | rel: 1 + // CHECK-NEXT: RelChild | TemplCls | c:@ST>1#T@TemplCls +}; diff --git a/tools/c-index-test/core_main.cpp b/tools/c-index-test/core_main.cpp index b0d40edd4bb..1c5c97f934b 100644 --- a/tools/c-index-test/core_main.cpp +++ b/tools/c-index-test/core_main.cpp @@ -157,10 +157,7 @@ static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS) { static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx, raw_ostream &OS) { - if (auto *ND = dyn_cast(D)) { - PrintingPolicy PrintPolicy(Ctx.getLangOpts()); - ND->getDeclName().print(OS, PrintPolicy); - } else { + if (printSymbolName(D, Ctx.getLangOpts(), OS)) { OS << ""; } OS << " | "; From 9e126b6b7362181c3f055da690281e439b4beb04 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Tue, 16 Feb 2016 05:39:33 +0000 Subject: [PATCH 366/742] [Frontend] Make sure WrapperFrontendAction updates CurrentInput after calling BeginSourceFileAction. I don't have a test case to add unfortunately. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260937 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Frontend/FrontendAction.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/Frontend/FrontendAction.cpp b/lib/Frontend/FrontendAction.cpp index 71fb17c25f5..82373e320ef 100644 --- a/lib/Frontend/FrontendAction.cpp +++ b/lib/Frontend/FrontendAction.cpp @@ -566,7 +566,10 @@ bool WrapperFrontendAction::BeginSourceFileAction(CompilerInstance &CI, StringRef Filename) { WrappedAction->setCurrentInput(getCurrentInput()); WrappedAction->setCompilerInstance(&CI); - return WrappedAction->BeginSourceFileAction(CI, Filename); + auto Ret = WrappedAction->BeginSourceFileAction(CI, Filename); + // BeginSourceFileAction may change CurrentInput, e.g. during module builds. + setCurrentInput(WrappedAction->getCurrentInput()); + return Ret; } void WrapperFrontendAction::ExecuteAction() { WrappedAction->ExecuteAction(); From a02f5f7a0f8142f5a7afc750cc466182a444a72e Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Sat, 20 Feb 2016 20:34:55 +0000 Subject: [PATCH 367/742] [c-index-test] CMake: When installing c-index-test to a different prefix directory, add an rpath so that it can find libclang. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261445 91177308-0d34-0410-b5e6-96231b3b80d8 --- tools/c-index-test/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/c-index-test/CMakeLists.txt b/tools/c-index-test/CMakeLists.txt index 3958e95a90f..e0df5031d3e 100644 --- a/tools/c-index-test/CMakeLists.txt +++ b/tools/c-index-test/CMakeLists.txt @@ -42,6 +42,8 @@ endif() if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) if(INTERNAL_INSTALL_PREFIX) set(INSTALL_DESTINATION "${INTERNAL_INSTALL_PREFIX}/bin") + set_property(TARGET c-index-test APPEND PROPERTY INSTALL_RPATH + "@executable_path/../../lib") else() set(INSTALL_DESTINATION bin) endif() From b6375da498eaf167b6ac14518d3cc34ae8ee99f1 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Mon, 29 Feb 2016 07:55:51 +0000 Subject: [PATCH 368/742] [index] Use ',' to separate symbol roles when printing. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262205 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Index/IndexSymbol.cpp | 2 +- test/Index/Core/index-source.cpp | 2 +- test/Index/Core/index-source.m | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Index/IndexSymbol.cpp b/lib/Index/IndexSymbol.cpp index 0106cd6134c..106d8c15fa4 100644 --- a/lib/Index/IndexSymbol.cpp +++ b/lib/Index/IndexSymbol.cpp @@ -214,7 +214,7 @@ void index::printSymbolRoles(SymbolRoleSet Roles, raw_ostream &OS) { bool VisitedOnce = false; applyForEachSymbolRole(Roles, [&](SymbolRole Role) { if (VisitedOnce) - OS << '/'; + OS << ','; else VisitedOnce = true; switch (Role) { diff --git a/test/Index/Core/index-source.cpp b/test/Index/Core/index-source.cpp index 75446468266..406f70f3896 100644 --- a/test/Index/Core/index-source.cpp +++ b/test/Index/Core/index-source.cpp @@ -4,6 +4,6 @@ template class TemplCls { // CHECK: [[@LINE-1]]:7 | c++-class/C++ | TemplCls | c:@ST>1#T@TemplCls | | Def | rel: 0 TemplCls(int x); - // CHECK: [[@LINE-1]]:3 | constructor/C++ | TemplCls | c:@ST>1#T@TemplCls@F@TemplCls#I# | | Decl/RelChild | rel: 1 + // CHECK: [[@LINE-1]]:3 | constructor/C++ | TemplCls | c:@ST>1#T@TemplCls@F@TemplCls#I# | | Decl,RelChild | rel: 1 // CHECK-NEXT: RelChild | TemplCls | c:@ST>1#T@TemplCls }; diff --git a/test/Index/Core/index-source.m b/test/Index/Core/index-source.m index 0fdae7ae5f9..6bff76ed27c 100644 --- a/test/Index/Core/index-source.m +++ b/test/Index/Core/index-source.m @@ -3,6 +3,6 @@ @interface Base // CHECK: [[@LINE-1]]:12 | objc-class/ObjC | Base | c:objc(cs)Base | _OBJC_CLASS_$_Base | Decl | rel: 0 -(void)meth; -// CHECK: [[@LINE-1]]:1 | objc-instance-method/ObjC | meth | c:objc(cs)Base(im)meth | -[Base meth] | Decl/Dyn/RelChild | rel: 1 +// CHECK: [[@LINE-1]]:1 | objc-instance-method/ObjC | meth | c:objc(cs)Base(im)meth | -[Base meth] | Decl,Dyn,RelChild | rel: 1 // CHECK-NEXT: RelChild | Base | c:objc(cs)Base @end From 77c7cb378cae4ec85e1bfd32dfdc017dc920a806 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Mon, 29 Feb 2016 07:55:55 +0000 Subject: [PATCH 369/742] [AST/RecursiveASTVisitor] Correction so that dataTraverseStmtPost will get called after the statement has been visited. Fixes the indexing client of this. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262206 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/RecursiveASTVisitor.h | 29 +++++++++++++++++-------- test/Index/Core/index-source.m | 10 +++++++++ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h index 42e67517a61..27cb7bd2b17 100644 --- a/include/clang/AST/RecursiveASTVisitor.h +++ b/include/clang/AST/RecursiveASTVisitor.h @@ -139,7 +139,9 @@ template class RecursiveASTVisitor { /// Parameters involving this type are used to implement data /// recursion over Stmts and Exprs within this class, and should /// typically not be explicitly specified by derived classes. - typedef SmallVectorImpl DataRecursionQueue; + /// The bool bit indicates whether the statement has been traversed or not. + typedef SmallVectorImpl> + DataRecursionQueue; /// \brief Return a reference to the derived class. Derived &getDerived() { return *static_cast(this); } @@ -558,23 +560,32 @@ bool RecursiveASTVisitor::TraverseStmt(Stmt *S, return true; if (Queue) { - Queue->push_back(S); + Queue->push_back({S, false}); return true; } - SmallVector LocalQueue; - LocalQueue.push_back(S); + SmallVector, 8> LocalQueue; + LocalQueue.push_back({S, false}); while (!LocalQueue.empty()) { - Stmt *CurrS = LocalQueue.pop_back_val(); + auto &CurrSAndVisited = LocalQueue.back(); + Stmt *CurrS = CurrSAndVisited.getPointer(); + bool Visited = CurrSAndVisited.getInt(); + if (Visited) { + LocalQueue.pop_back(); + TRY_TO(dataTraverseStmtPost(CurrS)); + continue; + } - size_t N = LocalQueue.size(); if (getDerived().dataTraverseStmtPre(CurrS)) { + CurrSAndVisited.setInt(true); + size_t N = LocalQueue.size(); TRY_TO(dataTraverseNode(CurrS, &LocalQueue)); - TRY_TO(dataTraverseStmtPost(CurrS)); + // Process new children in the order they were added. + std::reverse(LocalQueue.begin() + N, LocalQueue.end()); + } else { + LocalQueue.pop_back(); } - // Process new children in the order they were added. - std::reverse(LocalQueue.begin() + N, LocalQueue.end()); } return true; diff --git a/test/Index/Core/index-source.m b/test/Index/Core/index-source.m index 6bff76ed27c..3307ec54393 100644 --- a/test/Index/Core/index-source.m +++ b/test/Index/Core/index-source.m @@ -6,3 +6,13 @@ -(void)meth; // CHECK: [[@LINE-1]]:1 | objc-instance-method/ObjC | meth | c:objc(cs)Base(im)meth | -[Base meth] | Decl,Dyn,RelChild | rel: 1 // CHECK-NEXT: RelChild | Base | c:objc(cs)Base @end + +void foo(); +// CHECK: [[@LINE+1]]:6 | function/C | goo | c:@F@goo | _goo | Def | rel: 0 +void goo(Base *b) { + // CHECK: [[@LINE+1]]:3 | function/C | foo | c:@F@foo | _foo | Ref,Call | rel: 0 + foo(); + // CHECK: [[@LINE+2]]:6 | objc-instance-method/ObjC | meth | c:objc(cs)Base(im)meth | -[Base meth] | Ref,Call,Dyn,RelRec | rel: 1 + // CHECK-NEXT: RelRec | Base | c:objc(cs)Base + [b meth]; +} From 00567f8448be00e5cc44ea407eb81c1daa145dd4 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Mon, 29 Feb 2016 07:56:00 +0000 Subject: [PATCH 370/742] [index] Add a caller relation for a call reference. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262207 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Index/IndexSymbol.h | 3 ++- lib/Index/IndexBody.cpp | 45 ++++++++++++++++++++----------- lib/Index/IndexSymbol.cpp | 2 ++ test/Index/Core/index-source.m | 6 +++-- 4 files changed, 37 insertions(+), 19 deletions(-) diff --git a/include/clang/Index/IndexSymbol.h b/include/clang/Index/IndexSymbol.h index 506540b0e6e..484edda696f 100644 --- a/include/clang/Index/IndexSymbol.h +++ b/include/clang/Index/IndexSymbol.h @@ -88,8 +88,9 @@ enum class SymbolRole : uint16_t { RelationBaseOf = 1 << 10, RelationOverrideOf = 1 << 11, RelationReceivedBy = 1 << 12, + RelationCalledBy = 1 << 13, }; -static const unsigned SymbolRoleBitNum = 13; +static const unsigned SymbolRoleBitNum = 14; typedef unsigned SymbolRoleSet; /// Represents a relation to another symbol for a symbol occurrence. diff --git a/lib/Index/IndexBody.cpp b/lib/Index/IndexBody.cpp index f7164453db6..fda8388a324 100644 --- a/lib/Index/IndexBody.cpp +++ b/lib/Index/IndexBody.cpp @@ -88,7 +88,7 @@ class BodyIndexer : public RecursiveASTVisitor { } else if (auto CE = dyn_cast(Parent)) { if (CE->getCallee()->IgnoreParenCasts() == E) { - Roles |= (unsigned)SymbolRole::Call; + addCallRole(Roles, Relations); if (auto *ME = dyn_cast(E)) { if (auto *CXXMD = dyn_cast_or_null(ME->getMemberDecl())) if (CXXMD->isVirtual() && !ME->hasQualifier()) { @@ -120,6 +120,15 @@ class BodyIndexer : public RecursiveASTVisitor { return Roles; } + void addCallRole(SymbolRoleSet &Roles, + SmallVectorImpl &Relations) { + Roles |= (unsigned)SymbolRole::Call; + if (auto *FD = dyn_cast(ParentDC)) + Relations.emplace_back((unsigned)SymbolRole::RelationCalledBy, FD); + else if (auto *MD = dyn_cast(ParentDC)) + Relations.emplace_back((unsigned)SymbolRole::RelationCalledBy, MD); + } + bool VisitDeclRefExpr(DeclRefExpr *E) { SmallVector Relations; SymbolRoleSet Roles = getRolesForRef(E, Relations); @@ -169,11 +178,12 @@ class BodyIndexer : public RecursiveASTVisitor { }; if (ObjCMethodDecl *MD = E->getMethodDecl()) { - SymbolRoleSet Roles = (unsigned)SymbolRole::Call; + SymbolRoleSet Roles{}; + SmallVector Relations; + addCallRole(Roles, Relations); if (E->isImplicit()) Roles |= (unsigned)SymbolRole::Implicit; - SmallVector Relations; if (isDynamic(E)) { Roles |= (unsigned)SymbolRole::Dynamic; if (auto *RecD = E->getReceiverInterface()) @@ -206,39 +216,42 @@ class BodyIndexer : public RecursiveASTVisitor { Parent, ParentDC, SymbolRoleSet(), {}, E); } + bool passObjCLiteralMethodCall(const ObjCMethodDecl *MD, const Expr *E) { + SymbolRoleSet Roles{}; + SmallVector Relations; + addCallRole(Roles, Relations); + Roles |= (unsigned)SymbolRole::Implicit; + return IndexCtx.handleReference(MD, E->getLocStart(), + Parent, ParentDC, Roles, Relations, E); + } + bool VisitObjCBoxedExpr(ObjCBoxedExpr *E) { if (ObjCMethodDecl *MD = E->getBoxingMethod()) { - SymbolRoleSet Roles = (unsigned)SymbolRole::Call; - Roles |= (unsigned)SymbolRole::Implicit; - return IndexCtx.handleReference(MD, E->getLocStart(), - Parent, ParentDC, Roles, {}, E); + return passObjCLiteralMethodCall(MD, E); } return true; } bool VisitObjCDictionaryLiteral(ObjCDictionaryLiteral *E) { if (ObjCMethodDecl *MD = E->getDictWithObjectsMethod()) { - SymbolRoleSet Roles = (unsigned)SymbolRole::Call; - Roles |= (unsigned)SymbolRole::Implicit; - return IndexCtx.handleReference(MD, E->getLocStart(), - Parent, ParentDC, Roles, {}, E); + return passObjCLiteralMethodCall(MD, E); } return true; } bool VisitObjCArrayLiteral(ObjCArrayLiteral *E) { if (ObjCMethodDecl *MD = E->getArrayWithObjectsMethod()) { - SymbolRoleSet Roles = (unsigned)SymbolRole::Call; - Roles |= (unsigned)SymbolRole::Implicit; - return IndexCtx.handleReference(MD, E->getLocStart(), - Parent, ParentDC, Roles, {}, E); + return passObjCLiteralMethodCall(MD, E); } return true; } bool VisitCXXConstructExpr(CXXConstructExpr *E) { + SymbolRoleSet Roles{}; + SmallVector Relations; + addCallRole(Roles, Relations); return IndexCtx.handleReference(E->getConstructor(), E->getLocation(), - Parent, ParentDC, (unsigned)SymbolRole::Call, {}, E); + Parent, ParentDC, Roles, Relations, E); } bool TraverseCXXOperatorCallExpr(CXXOperatorCallExpr *E, diff --git a/lib/Index/IndexSymbol.cpp b/lib/Index/IndexSymbol.cpp index 106d8c15fa4..fab8533d275 100644 --- a/lib/Index/IndexSymbol.cpp +++ b/lib/Index/IndexSymbol.cpp @@ -206,6 +206,7 @@ void index::applyForEachSymbolRole(SymbolRoleSet Roles, APPLY_FOR_ROLE(RelationBaseOf); APPLY_FOR_ROLE(RelationOverrideOf); APPLY_FOR_ROLE(RelationReceivedBy); + APPLY_FOR_ROLE(RelationCalledBy); #undef APPLY_FOR_ROLE } @@ -231,6 +232,7 @@ void index::printSymbolRoles(SymbolRoleSet Roles, raw_ostream &OS) { case SymbolRole::RelationBaseOf: OS << "RelBase"; break; case SymbolRole::RelationOverrideOf: OS << "RelOver"; break; case SymbolRole::RelationReceivedBy: OS << "RelRec"; break; + case SymbolRole::RelationCalledBy: OS << "RelCall"; break; } }); } diff --git a/test/Index/Core/index-source.m b/test/Index/Core/index-source.m index 3307ec54393..c2604f67545 100644 --- a/test/Index/Core/index-source.m +++ b/test/Index/Core/index-source.m @@ -10,9 +10,11 @@ -(void)meth; void foo(); // CHECK: [[@LINE+1]]:6 | function/C | goo | c:@F@goo | _goo | Def | rel: 0 void goo(Base *b) { - // CHECK: [[@LINE+1]]:3 | function/C | foo | c:@F@foo | _foo | Ref,Call | rel: 0 + // CHECK: [[@LINE+2]]:3 | function/C | foo | c:@F@foo | _foo | Ref,Call,RelCall | rel: 1 + // CHECK-NEXT: RelCall | goo | c:@F@goo foo(); - // CHECK: [[@LINE+2]]:6 | objc-instance-method/ObjC | meth | c:objc(cs)Base(im)meth | -[Base meth] | Ref,Call,Dyn,RelRec | rel: 1 + // CHECK: [[@LINE+3]]:6 | objc-instance-method/ObjC | meth | c:objc(cs)Base(im)meth | -[Base meth] | Ref,Call,Dyn,RelRec,RelCall | rel: 2 + // CHECK-NEXT: RelCall | goo | c:@F@goo // CHECK-NEXT: RelRec | Base | c:objc(cs)Base [b meth]; } From da4690ba89961eb1c5c861242029fce04212f07f Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Mon, 29 Feb 2016 07:56:07 +0000 Subject: [PATCH 371/742] [index] Print and test module import references. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262208 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Index/IndexSymbol.cpp | 3 +++ lib/Index/IndexingContext.cpp | 9 +++++++-- test/Index/Core/Inputs/module/ModA.h | 2 ++ .../Index/Core/Inputs/module/module.modulemap | 1 + test/Index/Core/index-with-module.m | 12 +++++++++++ tools/c-index-test/core_main.cpp | 20 +++++++++++++++++++ 6 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 test/Index/Core/Inputs/module/ModA.h create mode 100644 test/Index/Core/Inputs/module/module.modulemap create mode 100644 test/Index/Core/index-with-module.m diff --git a/lib/Index/IndexSymbol.cpp b/lib/Index/IndexSymbol.cpp index fab8533d275..8853b08133b 100644 --- a/lib/Index/IndexSymbol.cpp +++ b/lib/Index/IndexSymbol.cpp @@ -53,6 +53,9 @@ SymbolInfo index::getSymbolInfo(const Decl *D) { } else { switch (D->getKind()) { + case Decl::Import: + Info.Kind = SymbolKind::Module; + break; case Decl::Typedef: Info.Kind = SymbolKind::Typedef; break; case Decl::Function: diff --git a/lib/Index/IndexingContext.cpp b/lib/Index/IndexingContext.cpp index 91fbbfb2684..330080e77ed 100644 --- a/lib/Index/IndexingContext.cpp +++ b/lib/Index/IndexingContext.cpp @@ -58,7 +58,12 @@ bool IndexingContext::handleReference(const NamedDecl *D, SourceLocation Loc, } bool IndexingContext::importedModule(const ImportDecl *ImportD) { - SourceLocation Loc = ImportD->getLocation(); + SourceLocation Loc; + auto IdLocs = ImportD->getIdentifierLocs(); + if (!IdLocs.empty()) + Loc = IdLocs.front(); + else + Loc = ImportD->getLocation(); SourceManager &SM = Ctx->getSourceManager(); Loc = SM.getFileLoc(Loc); if (Loc.isInvalid()) @@ -85,7 +90,7 @@ bool IndexingContext::importedModule(const ImportDecl *ImportD) { } } - SymbolRoleSet Roles{}; + SymbolRoleSet Roles = (unsigned)SymbolRole::Reference; if (ImportD->isImplicit()) Roles |= (unsigned)SymbolRole::Implicit; diff --git a/test/Index/Core/Inputs/module/ModA.h b/test/Index/Core/Inputs/module/ModA.h new file mode 100644 index 00000000000..081d86cc451 --- /dev/null +++ b/test/Index/Core/Inputs/module/ModA.h @@ -0,0 +1,2 @@ + +void ModA_func(void); diff --git a/test/Index/Core/Inputs/module/module.modulemap b/test/Index/Core/Inputs/module/module.modulemap new file mode 100644 index 00000000000..a132562eafd --- /dev/null +++ b/test/Index/Core/Inputs/module/module.modulemap @@ -0,0 +1 @@ +module ModA { header "ModA.h" export * } diff --git a/test/Index/Core/index-with-module.m b/test/Index/Core/index-with-module.m new file mode 100644 index 00000000000..646a48a2c93 --- /dev/null +++ b/test/Index/Core/index-with-module.m @@ -0,0 +1,12 @@ +// RUN: rm -rf %t.mcp +// RUN: c-index-test core -print-source-symbols -- %s -I %S/Inputs/module -fmodules -fmodules-cache-path=%t.mcp | FileCheck %s + +// CHECK: [[@LINE+1]]:9 | module/C | ModA | Ref | +@import ModA; +// CHECK: [[@LINE+1]]:1 | module/C | ModA | Ref,Impl | +#include "ModA.h" + +void foo() { + // CHECK: [[@LINE+1]]:3 | function/C | ModA_func | c:@F@ModA_func | {{.*}} | Ref,Call,RelCall | rel: 1 + ModA_func(); +} \ No newline at end of file diff --git a/tools/c-index-test/core_main.cpp b/tools/c-index-test/core_main.cpp index 1c5c97f934b..220160d8dc2 100644 --- a/tools/c-index-test/core_main.cpp +++ b/tools/c-index-test/core_main.cpp @@ -107,6 +107,26 @@ class PrintIndexDataConsumer : public IndexDataConsumer { return true; } + + bool handleModuleOccurence(const ImportDecl *ImportD, SymbolRoleSet Roles, + FileID FID, unsigned Offset) override { + ASTContext &Ctx = ImportD->getASTContext(); + SourceManager &SM = Ctx.getSourceManager(); + + unsigned Line = SM.getLineNumber(FID, Offset); + unsigned Col = SM.getColumnNumber(FID, Offset); + OS << Line << ':' << Col << " | "; + + printSymbolInfo(getSymbolInfo(ImportD), OS); + OS << " | "; + + OS << ImportD->getImportedModule()->getFullModuleName() << " | "; + + printSymbolRoles(Roles, OS); + OS << " |\n"; + + return true; + } }; } // anonymous namespace From 11b2f11120f963b55f1ea27ac287289b9ef8aff0 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Tue, 1 Mar 2016 02:46:32 +0000 Subject: [PATCH 372/742] [index] Fix issue where data visitation was disabled with C++ operator call expressions, during indexing. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262290 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Index/IndexBody.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Index/IndexBody.cpp b/lib/Index/IndexBody.cpp index fda8388a324..74e082a7cae 100644 --- a/lib/Index/IndexBody.cpp +++ b/lib/Index/IndexBody.cpp @@ -258,7 +258,7 @@ class BodyIndexer : public RecursiveASTVisitor { DataRecursionQueue *Q = nullptr) { if (E->getOperatorLoc().isInvalid()) return true; // implicit. - return base::TraverseCXXOperatorCallExpr(E); + return base::TraverseCXXOperatorCallExpr(E, Q); } bool VisitDeclStmt(DeclStmt *S) { @@ -325,7 +325,7 @@ class BodyIndexer : public RecursiveASTVisitor { auto visitForm = [&](InitListExpr *Form) { for (Stmt *SubStmt : Form->children()) { - if (!TraverseStmt(SubStmt)) + if (!TraverseStmt(SubStmt, Q)) return false; } return true; From 18de6df933c33339a77007c3516c0e82fdf5e6b7 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Thu, 3 Mar 2016 05:33:54 +0000 Subject: [PATCH 373/742] [index] Report references of ObjC super class/protocols in interfaces and protocols. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262584 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Index/IndexDecl.cpp | 37 ++++++++++++++++++++++++++++------ test/Index/Core/index-source.m | 20 ++++++++++++++++++ 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/lib/Index/IndexDecl.cpp b/lib/Index/IndexDecl.cpp index 76f68e564c9..af438f36843 100644 --- a/lib/Index/IndexDecl.cpp +++ b/lib/Index/IndexDecl.cpp @@ -14,6 +14,12 @@ using namespace clang; using namespace index; +#define TRY_TO(CALL_EXPR) \ + do { \ + if (!CALL_EXPR) \ + return false; \ + } while (0) + namespace { class IndexingDeclVisitor : public ConstDeclVisitor { @@ -196,11 +202,30 @@ class IndexingDeclVisitor : public ConstDeclVisitor { return true; } + bool handleReferencedProtocols(const ObjCProtocolList &ProtList, + const ObjCContainerDecl *ContD) { + ObjCInterfaceDecl::protocol_loc_iterator LI = ProtList.loc_begin(); + for (ObjCInterfaceDecl::protocol_iterator + I = ProtList.begin(), E = ProtList.end(); I != E; ++I, ++LI) { + SourceLocation Loc = *LI; + ObjCProtocolDecl *PD = *I; + TRY_TO(IndexCtx.handleReference(PD, Loc, ContD, ContD, + SymbolRoleSet(), + SymbolRelation{(unsigned)SymbolRole::RelationBaseOf, ContD})); + } + return true; + } + bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *D) { if (D->isThisDeclarationADefinition()) { - if (!IndexCtx.handleDecl(D)) - return false; - IndexCtx.indexDeclContext(D); + TRY_TO(IndexCtx.handleDecl(D)); + if (auto *SuperD = D->getSuperClass()) { + TRY_TO(IndexCtx.handleReference(SuperD, D->getSuperClassLoc(), D, D, + SymbolRoleSet(), + SymbolRelation{(unsigned)SymbolRole::RelationBaseOf, D})); + } + TRY_TO(handleReferencedProtocols(D->getReferencedProtocols(), D)); + TRY_TO(IndexCtx.indexDeclContext(D)); } else { return IndexCtx.handleReference(D, D->getLocation(), nullptr, nullptr, SymbolRoleSet()); @@ -210,9 +235,9 @@ class IndexingDeclVisitor : public ConstDeclVisitor { bool VisitObjCProtocolDecl(const ObjCProtocolDecl *D) { if (D->isThisDeclarationADefinition()) { - if (!IndexCtx.handleDecl(D)) - return false; - IndexCtx.indexDeclContext(D); + TRY_TO(IndexCtx.handleDecl(D)); + TRY_TO(handleReferencedProtocols(D->getReferencedProtocols(), D)); + TRY_TO(IndexCtx.indexDeclContext(D)); } else { return IndexCtx.handleReference(D, D->getLocation(), nullptr, nullptr, SymbolRoleSet()); diff --git a/test/Index/Core/index-source.m b/test/Index/Core/index-source.m index c2604f67545..766b6b198f2 100644 --- a/test/Index/Core/index-source.m +++ b/test/Index/Core/index-source.m @@ -18,3 +18,23 @@ void goo(Base *b) { // CHECK-NEXT: RelRec | Base | c:objc(cs)Base [b meth]; } + +// CHECK: [[@LINE+1]]:11 | objc-protocol/ObjC | Prot1 | c:objc(pl)Prot1 | | Decl | rel: 0 +@protocol Prot1 +@end + +// CHECK: [[@LINE+3]]:11 | objc-protocol/ObjC | Prot2 | c:objc(pl)Prot2 | | Decl | rel: 0 +// CHECK: [[@LINE+2]]:17 | objc-protocol/ObjC | Prot1 | c:objc(pl)Prot1 | | Ref,RelBase | rel: 1 +// CHECK-NEXT: RelBase | Prot2 | c:objc(pl)Prot2 +@protocol Prot2 +@end + +// CHECK: [[@LINE+7]]:12 | objc-class/ObjC | Sub | c:objc(cs)Sub | _OBJC_CLASS_$_Sub | Decl | rel: 0 +// CHECK: [[@LINE+6]]:18 | objc-class/ObjC | Base | c:objc(cs)Base | _OBJC_CLASS_$_Base | Ref,RelBase | rel: 1 +// CHECK-NEXT: RelBase | Sub | c:objc(cs)Sub +// CHECK: [[@LINE+4]]:23 | objc-protocol/ObjC | Prot2 | c:objc(pl)Prot2 | | Ref,RelBase | rel: 1 +// CHECK-NEXT: RelBase | Sub | c:objc(cs)Sub +// CHECK: [[@LINE+2]]:30 | objc-protocol/ObjC | Prot1 | c:objc(pl)Prot1 | | Ref,RelBase | rel: 1 +// CHECK-NEXT: RelBase | Sub | c:objc(cs)Sub +@interface Sub : Base +@end From 5986cecc48ef99ee5801deaeac89a0f6278c7edb Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Fri, 4 Mar 2016 04:24:32 +0000 Subject: [PATCH 374/742] [index] Ignore ObjCTypeParamDecls during indexing. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262686 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Index/IndexingContext.cpp | 3 +++ test/Index/Core/index-source.m | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/lib/Index/IndexingContext.cpp b/lib/Index/IndexingContext.cpp index 330080e77ed..183c28aa210 100644 --- a/lib/Index/IndexingContext.cpp +++ b/lib/Index/IndexingContext.cpp @@ -103,6 +103,9 @@ bool IndexingContext::isFunctionLocalDecl(const Decl *D) { if (isa(D)) return true; + if (isa(D)) + return true; + if (!D->getParentFunctionOrMethod()) return false; diff --git a/test/Index/Core/index-source.m b/test/Index/Core/index-source.m index 766b6b198f2..d57879c8988 100644 --- a/test/Index/Core/index-source.m +++ b/test/Index/Core/index-source.m @@ -38,3 +38,8 @@ @protocol Prot2 // CHECK-NEXT: RelBase | Sub | c:objc(cs)Sub @interface Sub : Base @end + +@interface NSArray : Base +// CHECK-NOT: ObjectType +-(ObjectType)getit; +@end From 709e5e5f8c75e7ecded0236149ac5074158e0dcc Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Fri, 4 Mar 2016 07:17:43 +0000 Subject: [PATCH 375/742] [index] In ObjC++ handle objc type parameters for function USRs. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262693 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Index/USRGeneration.cpp | 17 +++++++++++++++++ test/Index/Core/index-source.mm | 11 +++++++++++ 2 files changed, 28 insertions(+) create mode 100644 test/Index/Core/index-source.mm diff --git a/lib/Index/USRGeneration.cpp b/lib/Index/USRGeneration.cpp index 3dfc27df42f..e5ff1751489 100644 --- a/lib/Index/USRGeneration.cpp +++ b/lib/Index/USRGeneration.cpp @@ -659,6 +659,11 @@ void USRGenerator::VisitType(QualType T) { T = PT->getPointeeType(); continue; } + if (const ObjCObjectPointerType *OPT = T->getAs()) { + Out << '*'; + T = OPT->getPointeeType(); + continue; + } if (const RValueReferenceType *RT = T->getAs()) { Out << "&&"; T = RT->getPointeeType(); @@ -693,6 +698,18 @@ void USRGenerator::VisitType(QualType T) { VisitTagDecl(TT->getDecl()); return; } + if (const ObjCInterfaceType *OIT = T->getAs()) { + Out << '$'; + VisitObjCInterfaceDecl(OIT->getDecl()); + return; + } + if (const ObjCObjectType *OIT = T->getAs()) { + Out << 'Q'; + VisitType(OIT->getBaseType()); + for (auto *Prot : OIT->getProtocols()) + VisitObjCProtocolDecl(Prot); + return; + } if (const TemplateTypeParmType *TTP = T->getAs()) { Out << 't' << TTP->getDepth() << '.' << TTP->getIndex(); return; diff --git a/test/Index/Core/index-source.mm b/test/Index/Core/index-source.mm new file mode 100644 index 00000000000..049a0bdaf64 --- /dev/null +++ b/test/Index/Core/index-source.mm @@ -0,0 +1,11 @@ +// RUN: c-index-test core -print-source-symbols -- %s -target x86_64-apple-macosx10.7 | FileCheck %s + +@interface MyCls +@end + +@protocol P1,P2; + +// CHECK: [[@LINE+1]]:6 | function/C | foo | c:@F@foo#*$objc(cs)MyCls# | __Z3fooP5MyCls | Decl | rel: 0 +void foo(MyCls *o); +// CHECK: [[@LINE+1]]:6 | function/C | foo | c:@F@foo#*Qoobjc(pl)P1objc(pl)P2# | __Z3fooPU15objcproto2P12P211objc_object | Decl | rel: 0 +void foo(id o); From ce7a476c45210370ab06de2adc8782b1cfda734f Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Fri, 4 Mar 2016 07:17:48 +0000 Subject: [PATCH 376/742] [index] Include parameter types in the USRs for C functions marked with 'overloadable' attribute. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262694 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Index/USRGeneration.cpp | 3 ++- test/Index/Core/index-source.m | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/Index/USRGeneration.cpp b/lib/Index/USRGeneration.cpp index e5ff1751489..6249c54a67d 100644 --- a/lib/Index/USRGeneration.cpp +++ b/lib/Index/USRGeneration.cpp @@ -211,7 +211,8 @@ void USRGenerator::VisitFunctionDecl(const FunctionDecl *D) { D->getDeclName().print(Out, Policy); ASTContext &Ctx = *Context; - if (!Ctx.getLangOpts().CPlusPlus || D->isExternC()) + if ((!Ctx.getLangOpts().CPlusPlus || D->isExternC()) && + !D->hasAttr()) return; if (const TemplateArgumentList * diff --git a/test/Index/Core/index-source.m b/test/Index/Core/index-source.m index d57879c8988..d1326624630 100644 --- a/test/Index/Core/index-source.m +++ b/test/Index/Core/index-source.m @@ -43,3 +43,8 @@ @interface NSArray : Base // CHECK-NOT: ObjectType -(ObjectType)getit; @end + +// CHECK: [[@LINE+1]]:6 | function/C | over_func | c:@F@over_func#I# | __Z9over_funci | Decl | rel: 0 +void over_func(int x) __attribute__((overloadable)); +// CHECK: [[@LINE+1]]:6 | function/C | over_func | c:@F@over_func#f# | __Z9over_funcf | Decl | rel: 0 +void over_func(float x) __attribute__((overloadable)); From 77784f8ec3366a1ab75429c0ea2835eff6ca38e0 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Fri, 4 Mar 2016 07:17:53 +0000 Subject: [PATCH 377/742] [index] Distinguish USRs of anonymous enums by using their first enumerator. rdar://24609949. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262695 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Index/IndexingContext.cpp | 4 ---- lib/Index/USRGeneration.cpp | 13 +++++++++++-- test/Index/Core/index-source.m | 17 +++++++++++++++++ test/Index/usrs.m | 12 ++++++------ 4 files changed, 34 insertions(+), 12 deletions(-) diff --git a/lib/Index/IndexingContext.cpp b/lib/Index/IndexingContext.cpp index 183c28aa210..002924ed71f 100644 --- a/lib/Index/IndexingContext.cpp +++ b/lib/Index/IndexingContext.cpp @@ -205,10 +205,6 @@ static const Decl *adjustParent(const Decl *Parent) { if (auto NS = dyn_cast(Parent)) { if (NS->isAnonymousNamespace()) continue; - } else if (auto EnumD = dyn_cast(Parent)) { - // Move enumerators under anonymous enum to the enclosing parent. - if (EnumD->getDeclName().isEmpty()) - continue; } else if (auto RD = dyn_cast(Parent)) { if (RD->isAnonymousStructOrUnion()) continue; diff --git a/lib/Index/USRGeneration.cpp b/lib/Index/USRGeneration.cpp index 6249c54a67d..86873f27e2d 100644 --- a/lib/Index/USRGeneration.cpp +++ b/lib/Index/USRGeneration.cpp @@ -421,7 +421,8 @@ void USRGenerator::VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *D) { void USRGenerator::VisitTagDecl(const TagDecl *D) { // Add the location of the tag decl to handle resolution across // translation units. - if (ShouldGenerateLocation(D) && GenLoc(D, /*IncludeOffset=*/isLocal(D))) + if (!isa(D) && + ShouldGenerateLocation(D) && GenLoc(D, /*IncludeOffset=*/isLocal(D))) return; D = D->getCanonicalDecl(); @@ -477,8 +478,16 @@ void USRGenerator::VisitTagDecl(const TagDecl *D) { else { if (D->isEmbeddedInDeclarator() && !D->isFreeStanding()) { printLoc(Out, D->getLocation(), Context->getSourceManager(), true); - } else + } else { Buf[off] = 'a'; + if (auto *ED = dyn_cast(D)) { + // Distinguish USRs of anonymous enums by using their first enumerator. + auto enum_range = ED->enumerators(); + if (enum_range.begin() != enum_range.end()) { + Out << '@' << **enum_range.begin(); + } + } + } } } diff --git a/test/Index/Core/index-source.m b/test/Index/Core/index-source.m index d1326624630..d70974ca034 100644 --- a/test/Index/Core/index-source.m +++ b/test/Index/Core/index-source.m @@ -48,3 +48,20 @@ -(ObjectType)getit; void over_func(int x) __attribute__((overloadable)); // CHECK: [[@LINE+1]]:6 | function/C | over_func | c:@F@over_func#f# | __Z9over_funcf | Decl | rel: 0 void over_func(float x) __attribute__((overloadable)); + +// CHECK: [[@LINE+1]]:6 | enum/C | MyEnum | c:@E@MyEnum | | Def | rel: 0 +enum MyEnum { + // CHECK: [[@LINE+2]]:3 | enumerator/C | EnumeratorInNamed | c:@E@MyEnum@EnumeratorInNamed | | Def,RelChild | rel: 1 + // CHECK-NEXT: RelChild | MyEnum | c:@E@MyEnum + EnumeratorInNamed +}; + +// CHECK: [[@LINE+1]]:1 | enum/C | | c:@Ea@One | | Def | rel: 0 +enum { + // CHECK: [[@LINE+2]]:3 | enumerator/C | One | c:@Ea@One@One | | Def,RelChild | rel: 1 + // CHECK-NEXT: RelChild | | c:@Ea@One + One, + // CHECK: [[@LINE+2]]:3 | enumerator/C | Two | c:@Ea@One@Two | | Def,RelChild | rel: 1 + // CHECK-NEXT: RelChild | | c:@Ea@One + Two, +}; diff --git a/test/Index/usrs.m b/test/Index/usrs.m index fc3fbc91057..92c3a3fafee 100644 --- a/test/Index/usrs.m +++ b/test/Index/usrs.m @@ -110,12 +110,12 @@ -(int)methodWithFn:(void (*)(int *p))fn; // CHECK: usrs.m c:usrs.m@F@my_helper Extent=[3:1 - 3:60] // CHECK: usrs.m c:usrs.m@95@F@my_helper@x Extent=[3:29 - 3:34] // CHECK: usrs.m c:usrs.m@102@F@my_helper@y Extent=[3:36 - 3:41] -// CHECK: usrs.m c:usrs.m@Ea Extent=[5:1 - 8:2] -// CHECK: usrs.m c:usrs.m@Ea@ABA Extent=[6:3 - 6:6] -// CHECK: usrs.m c:usrs.m@Ea@CADABA Extent=[7:3 - 7:9] -// CHECK: usrs.m c:usrs.m@Ea Extent=[10:1 - 13:2] -// CHECK: usrs.m c:usrs.m@Ea@FOO Extent=[11:3 - 11:6] -// CHECK: usrs.m c:usrs.m@Ea@BAR Extent=[12:3 - 12:6] +// CHECK: usrs.m c:@Ea@ABA Extent=[5:1 - 8:2] +// CHECK: usrs.m c:@Ea@ABA@ABA Extent=[6:3 - 6:6] +// CHECK: usrs.m c:@Ea@ABA@CADABA Extent=[7:3 - 7:9] +// CHECK: usrs.m c:@Ea@FOO Extent=[10:1 - 13:2] +// CHECK: usrs.m c:@Ea@FOO@FOO Extent=[11:3 - 11:6] +// CHECK: usrs.m c:@Ea@FOO@BAR Extent=[12:3 - 12:6] // CHECK: usrs.m c:@SA@MyStruct Extent=[15:9 - 18:2] // CHECK: usrs.m c:@SA@MyStruct@FI@wa Extent=[16:3 - 16:9] // CHECK: usrs.m c:@SA@MyStruct@FI@moo Extent=[17:3 - 17:10] From 4fc87c1e9fb159d7e2714fff765b6507b68c2c3e Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Wed, 9 Mar 2016 02:12:40 +0000 Subject: [PATCH 378/742] [index] Fix assertion hit when indexing re-declarations of built-in functions. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262984 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Index/IndexingContext.cpp | 1 + test/Index/Core/index-source.m | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/lib/Index/IndexingContext.cpp b/lib/Index/IndexingContext.cpp index 002924ed71f..59d5d9ad76d 100644 --- a/lib/Index/IndexingContext.cpp +++ b/lib/Index/IndexingContext.cpp @@ -297,6 +297,7 @@ bool IndexingContext::handleDeclOccurrence(const Decl *D, SourceLocation Loc, if (Parent) Parent = getCanonicalDecl(Parent); assert(!Parent || !Parent->isImplicit() || + isa(Parent) || isa(Parent) || isa(Parent)); SmallVector FinalRelations; diff --git a/test/Index/Core/index-source.m b/test/Index/Core/index-source.m index d70974ca034..6248f18bfb5 100644 --- a/test/Index/Core/index-source.m +++ b/test/Index/Core/index-source.m @@ -65,3 +65,8 @@ -(ObjectType)getit; // CHECK-NEXT: RelChild | | c:@Ea@One Two, }; + +// CHECK: [[@LINE+1]]:13 | typedef/C | jmp_buf | c:index-source.m@T@jmp_buf | | Def | rel: 0 +typedef int jmp_buf[(18)]; +// CHECK: [[@LINE+1]]:19 | typedef/C | jmp_buf | c:index-source.m@T@jmp_buf | | Ref | rel: 0 +extern int setjmp(jmp_buf); From a6d77410ccf7177510502989f6bcc5d8e7c89271 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Wed, 9 Mar 2016 02:12:46 +0000 Subject: [PATCH 379/742] [index] libclang: Make sure to treat forward ObjC protocols as ObjCProtocolRef declarations, and fix related crash. rdar://25035376 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262985 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Index/IndexDecl.cpp | 8 ++++---- test/Index/index-refs.m | 5 +++++ tools/libclang/CXIndexDataConsumer.cpp | 8 ++++++++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/lib/Index/IndexDecl.cpp b/lib/Index/IndexDecl.cpp index af438f36843..5f5c49a2e33 100644 --- a/lib/Index/IndexDecl.cpp +++ b/lib/Index/IndexDecl.cpp @@ -227,8 +227,8 @@ class IndexingDeclVisitor : public ConstDeclVisitor { TRY_TO(handleReferencedProtocols(D->getReferencedProtocols(), D)); TRY_TO(IndexCtx.indexDeclContext(D)); } else { - return IndexCtx.handleReference(D, D->getLocation(), nullptr, nullptr, - SymbolRoleSet()); + return IndexCtx.handleReference(D, D->getLocation(), nullptr, + D->getDeclContext(), SymbolRoleSet()); } return true; } @@ -239,8 +239,8 @@ class IndexingDeclVisitor : public ConstDeclVisitor { TRY_TO(handleReferencedProtocols(D->getReferencedProtocols(), D)); TRY_TO(IndexCtx.indexDeclContext(D)); } else { - return IndexCtx.handleReference(D, D->getLocation(), nullptr, nullptr, - SymbolRoleSet()); + return IndexCtx.handleReference(D, D->getLocation(), nullptr, + D->getDeclContext(), SymbolRoleSet()); } return true; } diff --git a/test/Index/index-refs.m b/test/Index/index-refs.m index f25013b882f..457712bcbc7 100644 --- a/test/Index/index-refs.m +++ b/test/Index/index-refs.m @@ -21,7 +21,12 @@ void foo2() { [I clsMeth]; } +@protocol ForwardProt; + // RUN: c-index-test -index-file %s | FileCheck %s // CHECK: [indexEntityReference]: kind: objc-protocol | name: Prot | {{.*}} | loc: 12:27 // CHECK: [indexEntityReference]: kind: struct | name: FooS | {{.*}} | loc: 13:18 // CHECK: [indexEntityReference]: kind: objc-class | name: I | {{.*}} | loc: 21:4 + +// CHECK: [indexDeclaration]: kind: objc-protocol | name: ForwardProt | {{.*}} | loc: 24:11 +// CHECK-NEXT: : kind: forward-ref diff --git a/tools/libclang/CXIndexDataConsumer.cpp b/tools/libclang/CXIndexDataConsumer.cpp index f6f8e306794..911c79c6e23 100644 --- a/tools/libclang/CXIndexDataConsumer.cpp +++ b/tools/libclang/CXIndexDataConsumer.cpp @@ -171,6 +171,14 @@ bool CXIndexDataConsumer::handleDeclOccurence(const Decl *D, return true; } } + if (auto *ObjCPD = dyn_cast_or_null(ASTNode.OrigD)) { + if (!ObjCPD->isThisDeclarationADefinition() && + ObjCPD->getLocation() == Loc) { + // The libclang API treats this as ObjCProtocolRef declaration. + IndexingDeclVisitor(*this, Loc, nullptr).Visit(ObjCPD); + return true; + } + } CXIdxEntityRefKind Kind = CXIdxEntityRef_Direct; if (Roles & (unsigned)SymbolRole::Implicit) { From 620021a21d0e4f4015dbfe7f6975f30dc01ce64d Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Wed, 9 Mar 2016 23:31:34 +0000 Subject: [PATCH 380/742] [Modules] Add stdatomic to the list of builtin headers Since it's provided by the compiler. This allows a system module map file to declare a module for it. No test change for cstd.m, since stdatomic.h doesn't function without a relatively complete stdint.h and stddef.h, which tests using this module don't provide. rdar://problem/24931246 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263076 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Lex/ModuleMap.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp index e14f8766ab1..ac766a97c9e 100644 --- a/lib/Lex/ModuleMap.cpp +++ b/lib/Lex/ModuleMap.cpp @@ -154,6 +154,7 @@ static bool isBuiltinHeader(StringRef FileName) { .Case("limits.h", true) .Case("stdalign.h", true) .Case("stdarg.h", true) + .Case("stdatomic.h", true) .Case("stdbool.h", true) .Case("stddef.h", true) .Case("stdint.h", true) From d715fdfd8cc8e6361b80f8f7a11521651877d01f Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Wed, 9 Mar 2016 02:53:12 +0000 Subject: [PATCH 381/742] [index] Add a message for the assertion, NFC. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262991 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Index/IndexingContext.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Index/IndexingContext.cpp b/lib/Index/IndexingContext.cpp index 59d5d9ad76d..c1ebed58ce6 100644 --- a/lib/Index/IndexingContext.cpp +++ b/lib/Index/IndexingContext.cpp @@ -298,7 +298,8 @@ bool IndexingContext::handleDeclOccurrence(const Decl *D, SourceLocation Loc, Parent = getCanonicalDecl(Parent); assert(!Parent || !Parent->isImplicit() || isa(Parent) || - isa(Parent) || isa(Parent)); + isa(Parent) || isa(Parent) && + "unexpected implicit parent!"); SmallVector FinalRelations; FinalRelations.reserve(Relations.size()+1); From ceda566233f352cbf75b1ebc5d94ceb4c5552db0 Mon Sep 17 00:00:00 2001 From: Manuel Klimek Date: Wed, 9 Mar 2016 10:06:45 +0000 Subject: [PATCH 382/742] Pacify gcc's parenthesis warning, which doesn't realize that parens don't matter here. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263004 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Index/IndexingContext.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/Index/IndexingContext.cpp b/lib/Index/IndexingContext.cpp index c1ebed58ce6..188e10f019a 100644 --- a/lib/Index/IndexingContext.cpp +++ b/lib/Index/IndexingContext.cpp @@ -296,9 +296,8 @@ bool IndexingContext::handleDeclOccurrence(const Decl *D, SourceLocation Loc, Parent = adjustParent(Parent); if (Parent) Parent = getCanonicalDecl(Parent); - assert(!Parent || !Parent->isImplicit() || - isa(Parent) || - isa(Parent) || isa(Parent) && + assert((!Parent || !Parent->isImplicit() || isa(Parent) || + isa(Parent) || isa(Parent)) && "unexpected implicit parent!"); SmallVector FinalRelations; From af91354cf9d3cd4ea6942a083bc101934e0c57b3 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Thu, 17 Mar 2016 04:28:19 +0000 Subject: [PATCH 383/742] [index] Make sure that declarations of builtin functions are indexed. rdar://25154630 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263689 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Index/IndexingContext.cpp | 7 +++++-- test/Index/Core/index-source.m | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/Index/IndexingContext.cpp b/lib/Index/IndexingContext.cpp index 188e10f019a..5c2cb246737 100644 --- a/lib/Index/IndexingContext.cpp +++ b/lib/Index/IndexingContext.cpp @@ -289,14 +289,17 @@ bool IndexingContext::handleDeclOccurrence(const Decl *D, SourceLocation Loc, Roles |= (unsigned)SymbolRole::Declaration; D = getCanonicalDecl(D); - if (D->isImplicit() && !isa(D)) { + if (D->isImplicit() && !isa(D) && + !(isa(D) && cast(D)->getBuiltinID())) { // operator new declarations will link to the implicit one as canonical. return true; } Parent = adjustParent(Parent); if (Parent) Parent = getCanonicalDecl(Parent); - assert((!Parent || !Parent->isImplicit() || isa(Parent) || + assert((!Parent || !Parent->isImplicit() || + (isa(Parent) && + cast(Parent)->getBuiltinID()) || isa(Parent) || isa(Parent)) && "unexpected implicit parent!"); diff --git a/test/Index/Core/index-source.m b/test/Index/Core/index-source.m index 6248f18bfb5..ac309c8e696 100644 --- a/test/Index/Core/index-source.m +++ b/test/Index/Core/index-source.m @@ -68,5 +68,6 @@ -(ObjectType)getit; // CHECK: [[@LINE+1]]:13 | typedef/C | jmp_buf | c:index-source.m@T@jmp_buf | | Def | rel: 0 typedef int jmp_buf[(18)]; +// CHECK: [[@LINE+2]]:12 | function/C | setjmp | c:@F@setjmp | _setjmp | Decl | rel: 0 // CHECK: [[@LINE+1]]:19 | typedef/C | jmp_buf | c:index-source.m@T@jmp_buf | | Ref | rel: 0 extern int setjmp(jmp_buf); From 4a0c658363fd1cb7718e123d07695f26ab13e58a Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Fri, 25 Mar 2016 17:01:59 +0000 Subject: [PATCH 384/742] [index] Remove redundancy between symbol kind and language Condense the ObjCKIND and CXXKIND options into just KIND, since the language was already specified on a per-symbol basis and this information was redundant. This only changes the internal representation; naturally the libclang interface remains the same. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@264423 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Index/IndexSymbol.h | 37 +++++------ lib/Index/IndexSymbol.cpp | 89 +++++++++++++------------- test/Index/Core/index-source.cpp | 2 +- test/Index/Core/index-source.m | 24 +++---- tools/libclang/CXIndexDataConsumer.cpp | 58 ++++++++++------- 5 files changed, 107 insertions(+), 103 deletions(-) diff --git a/include/clang/Index/IndexSymbol.h b/include/clang/Index/IndexSymbol.h index 484edda696f..56b12fa6bec 100644 --- a/include/clang/Index/IndexSymbol.h +++ b/include/clang/Index/IndexSymbol.h @@ -24,38 +24,33 @@ enum class SymbolKind : uint8_t { Unknown, Module, + Namespace, + NamespaceAlias, Macro, Enum, Struct, + Class, + Protocol, + Extension, Union, - Typedef, + TypeAlias, Function, Variable, Field, EnumConstant, - ObjCClass, - ObjCProtocol, - ObjCCategory, - - ObjCInstanceMethod, - ObjCClassMethod, - ObjCProperty, - ObjCIvar, - - CXXClass, - CXXNamespace, - CXXNamespaceAlias, - CXXStaticVariable, - CXXStaticMethod, - CXXInstanceMethod, - CXXConstructor, - CXXDestructor, - CXXConversionFunction, - CXXTypeAlias, - CXXInterface, + InstanceMethod, + ClassMethod, + StaticMethod, + InstanceProperty, + ClassProperty, + StaticProperty, + + Constructor, + Destructor, + ConversionFunction, }; enum class SymbolLanguage { diff --git a/lib/Index/IndexSymbol.cpp b/lib/Index/IndexSymbol.cpp index 8853b08133b..8465d3baff1 100644 --- a/lib/Index/IndexSymbol.cpp +++ b/lib/Index/IndexSymbol.cpp @@ -30,11 +30,11 @@ SymbolInfo index::getSymbolInfo(const Decl *D) { case TTK_Union: Info.Kind = SymbolKind::Union; break; case TTK_Class: - Info.Kind = SymbolKind::CXXClass; + Info.Kind = SymbolKind::Class; Info.Lang = SymbolLanguage::CXX; break; case TTK_Interface: - Info.Kind = SymbolKind::CXXInterface; + Info.Kind = SymbolKind::Protocol; Info.Lang = SymbolLanguage::CXX; break; case TTK_Enum: @@ -57,7 +57,7 @@ SymbolInfo index::getSymbolInfo(const Decl *D) { Info.Kind = SymbolKind::Module; break; case Decl::Typedef: - Info.Kind = SymbolKind::Typedef; break; + Info.Kind = SymbolKind::TypeAlias; break; // Lang = C case Decl::Function: Info.Kind = SymbolKind::Function; break; @@ -67,7 +67,7 @@ SymbolInfo index::getSymbolInfo(const Decl *D) { case Decl::Var: Info.Kind = SymbolKind::Variable; if (isa(D->getDeclContext())) { - Info.Kind = SymbolKind::CXXStaticVariable; + Info.Kind = SymbolKind::StaticProperty; Info.Lang = SymbolLanguage::CXX; } break; @@ -83,91 +83,94 @@ SymbolInfo index::getSymbolInfo(const Decl *D) { Info.Kind = SymbolKind::EnumConstant; break; case Decl::ObjCInterface: case Decl::ObjCImplementation: - Info.Kind = SymbolKind::ObjCClass; + Info.Kind = SymbolKind::Class; Info.Lang = SymbolLanguage::ObjC; break; case Decl::ObjCProtocol: - Info.Kind = SymbolKind::ObjCProtocol; + Info.Kind = SymbolKind::Protocol; Info.Lang = SymbolLanguage::ObjC; break; case Decl::ObjCCategory: case Decl::ObjCCategoryImpl: - Info.Kind = SymbolKind::ObjCCategory; + Info.Kind = SymbolKind::Extension; Info.Lang = SymbolLanguage::ObjC; break; case Decl::ObjCMethod: if (cast(D)->isInstanceMethod()) - Info.Kind = SymbolKind::ObjCInstanceMethod; + Info.Kind = SymbolKind::InstanceMethod; else - Info.Kind = SymbolKind::ObjCClassMethod; + Info.Kind = SymbolKind::ClassMethod; Info.Lang = SymbolLanguage::ObjC; break; case Decl::ObjCProperty: - Info.Kind = SymbolKind::ObjCProperty; + Info.Kind = SymbolKind::InstanceProperty; Info.Lang = SymbolLanguage::ObjC; break; case Decl::ObjCIvar: - Info.Kind = SymbolKind::ObjCIvar; + Info.Kind = SymbolKind::Field; Info.Lang = SymbolLanguage::ObjC; break; case Decl::Namespace: - Info.Kind = SymbolKind::CXXNamespace; + Info.Kind = SymbolKind::Namespace; Info.Lang = SymbolLanguage::CXX; break; case Decl::NamespaceAlias: - Info.Kind = SymbolKind::CXXNamespaceAlias; + Info.Kind = SymbolKind::NamespaceAlias; Info.Lang = SymbolLanguage::CXX; break; case Decl::CXXConstructor: - Info.Kind = SymbolKind::CXXConstructor; + Info.Kind = SymbolKind::Constructor; Info.Lang = SymbolLanguage::CXX; break; case Decl::CXXDestructor: - Info.Kind = SymbolKind::CXXDestructor; + Info.Kind = SymbolKind::Destructor; Info.Lang = SymbolLanguage::CXX; break; case Decl::CXXConversion: - Info.Kind = SymbolKind::CXXConversionFunction; + Info.Kind = SymbolKind::ConversionFunction; Info.Lang = SymbolLanguage::CXX; break; case Decl::CXXMethod: { const CXXMethodDecl *MD = cast(D); if (MD->isStatic()) - Info.Kind = SymbolKind::CXXStaticMethod; + Info.Kind = SymbolKind::StaticMethod; else - Info.Kind = SymbolKind::CXXInstanceMethod; + Info.Kind = SymbolKind::InstanceMethod; Info.Lang = SymbolLanguage::CXX; break; } case Decl::ClassTemplate: - Info.Kind = SymbolKind::CXXClass; + Info.Kind = SymbolKind::Class; Info.TemplateKind = SymbolCXXTemplateKind::Template; + Info.Lang = SymbolLanguage::CXX; break; case Decl::FunctionTemplate: Info.Kind = SymbolKind::Function; Info.TemplateKind = SymbolCXXTemplateKind::Template; + Info.Lang = SymbolLanguage::CXX; if (const CXXMethodDecl *MD = dyn_cast_or_null( cast(D)->getTemplatedDecl())) { if (isa(MD)) - Info.Kind = SymbolKind::CXXConstructor; + Info.Kind = SymbolKind::Constructor; else if (isa(MD)) - Info.Kind = SymbolKind::CXXDestructor; + Info.Kind = SymbolKind::Destructor; else if (isa(MD)) - Info.Kind = SymbolKind::CXXConversionFunction; + Info.Kind = SymbolKind::ConversionFunction; else { if (MD->isStatic()) - Info.Kind = SymbolKind::CXXStaticMethod; + Info.Kind = SymbolKind::StaticMethod; else - Info.Kind = SymbolKind::CXXInstanceMethod; + Info.Kind = SymbolKind::InstanceMethod; } } break; case Decl::TypeAliasTemplate: - Info.Kind = SymbolKind::CXXTypeAlias; + Info.Kind = SymbolKind::TypeAlias; + Info.Lang = SymbolLanguage::CXX; Info.TemplateKind = SymbolCXXTemplateKind::Template; break; case Decl::TypeAlias: - Info.Kind = SymbolKind::CXXTypeAlias; + Info.Kind = SymbolKind::TypeAlias; Info.Lang = SymbolLanguage::CXX; break; default: @@ -262,33 +265,29 @@ StringRef index::getSymbolKindString(SymbolKind K) { switch (K) { case SymbolKind::Unknown: return ""; case SymbolKind::Module: return "module"; + case SymbolKind::Namespace: return "namespace"; + case SymbolKind::NamespaceAlias: return "namespace-alias"; case SymbolKind::Macro: return "macro"; case SymbolKind::Enum: return "enum"; case SymbolKind::Struct: return "struct"; + case SymbolKind::Class: return "class"; + case SymbolKind::Protocol: return "protocol"; + case SymbolKind::Extension: return "extension"; case SymbolKind::Union: return "union"; - case SymbolKind::Typedef: return "typedef"; + case SymbolKind::TypeAlias: return "type-alias"; case SymbolKind::Function: return "function"; case SymbolKind::Variable: return "variable"; case SymbolKind::Field: return "field"; case SymbolKind::EnumConstant: return "enumerator"; - case SymbolKind::ObjCClass: return "objc-class"; - case SymbolKind::ObjCProtocol: return "objc-protocol"; - case SymbolKind::ObjCCategory: return "objc-category"; - case SymbolKind::ObjCInstanceMethod: return "objc-instance-method"; - case SymbolKind::ObjCClassMethod: return "objc-class-method"; - case SymbolKind::ObjCProperty: return "objc-property"; - case SymbolKind::ObjCIvar: return "objc-ivar"; - case SymbolKind::CXXClass: return "c++-class"; - case SymbolKind::CXXNamespace: return "namespace"; - case SymbolKind::CXXNamespaceAlias: return "namespace-alias"; - case SymbolKind::CXXStaticVariable: return "c++-static-var"; - case SymbolKind::CXXStaticMethod: return "c++-static-method"; - case SymbolKind::CXXInstanceMethod: return "c++-instance-method"; - case SymbolKind::CXXConstructor: return "constructor"; - case SymbolKind::CXXDestructor: return "destructor"; - case SymbolKind::CXXConversionFunction: return "coversion-func"; - case SymbolKind::CXXTypeAlias: return "type-alias"; - case SymbolKind::CXXInterface: return "c++-__interface"; + case SymbolKind::InstanceMethod: return "instance-method"; + case SymbolKind::ClassMethod: return "class-method"; + case SymbolKind::StaticMethod: return "static-method"; + case SymbolKind::InstanceProperty: return "instance-property"; + case SymbolKind::ClassProperty: return "class-property"; + case SymbolKind::StaticProperty: return "static-property"; + case SymbolKind::Constructor: return "constructor"; + case SymbolKind::Destructor: return "destructor"; + case SymbolKind::ConversionFunction: return "coversion-func"; } } diff --git a/test/Index/Core/index-source.cpp b/test/Index/Core/index-source.cpp index 406f70f3896..75c6396da55 100644 --- a/test/Index/Core/index-source.cpp +++ b/test/Index/Core/index-source.cpp @@ -2,7 +2,7 @@ template class TemplCls { -// CHECK: [[@LINE-1]]:7 | c++-class/C++ | TemplCls | c:@ST>1#T@TemplCls | | Def | rel: 0 +// CHECK: [[@LINE-1]]:7 | class/C++ | TemplCls | c:@ST>1#T@TemplCls | | Def | rel: 0 TemplCls(int x); // CHECK: [[@LINE-1]]:3 | constructor/C++ | TemplCls | c:@ST>1#T@TemplCls@F@TemplCls#I# | | Decl,RelChild | rel: 1 // CHECK-NEXT: RelChild | TemplCls | c:@ST>1#T@TemplCls diff --git a/test/Index/Core/index-source.m b/test/Index/Core/index-source.m index ac309c8e696..0869c0ac7e1 100644 --- a/test/Index/Core/index-source.m +++ b/test/Index/Core/index-source.m @@ -1,9 +1,9 @@ // RUN: c-index-test core -print-source-symbols -- %s -target x86_64-apple-macosx10.7 | FileCheck %s @interface Base -// CHECK: [[@LINE-1]]:12 | objc-class/ObjC | Base | c:objc(cs)Base | _OBJC_CLASS_$_Base | Decl | rel: 0 +// CHECK: [[@LINE-1]]:12 | class/ObjC | Base | c:objc(cs)Base | _OBJC_CLASS_$_Base | Decl | rel: 0 -(void)meth; -// CHECK: [[@LINE-1]]:1 | objc-instance-method/ObjC | meth | c:objc(cs)Base(im)meth | -[Base meth] | Decl,Dyn,RelChild | rel: 1 +// CHECK: [[@LINE-1]]:1 | instance-method/ObjC | meth | c:objc(cs)Base(im)meth | -[Base meth] | Decl,Dyn,RelChild | rel: 1 // CHECK-NEXT: RelChild | Base | c:objc(cs)Base @end @@ -13,28 +13,28 @@ void goo(Base *b) { // CHECK: [[@LINE+2]]:3 | function/C | foo | c:@F@foo | _foo | Ref,Call,RelCall | rel: 1 // CHECK-NEXT: RelCall | goo | c:@F@goo foo(); - // CHECK: [[@LINE+3]]:6 | objc-instance-method/ObjC | meth | c:objc(cs)Base(im)meth | -[Base meth] | Ref,Call,Dyn,RelRec,RelCall | rel: 2 + // CHECK: [[@LINE+3]]:6 | instance-method/ObjC | meth | c:objc(cs)Base(im)meth | -[Base meth] | Ref,Call,Dyn,RelRec,RelCall | rel: 2 // CHECK-NEXT: RelCall | goo | c:@F@goo // CHECK-NEXT: RelRec | Base | c:objc(cs)Base [b meth]; } -// CHECK: [[@LINE+1]]:11 | objc-protocol/ObjC | Prot1 | c:objc(pl)Prot1 | | Decl | rel: 0 +// CHECK: [[@LINE+1]]:11 | protocol/ObjC | Prot1 | c:objc(pl)Prot1 | | Decl | rel: 0 @protocol Prot1 @end -// CHECK: [[@LINE+3]]:11 | objc-protocol/ObjC | Prot2 | c:objc(pl)Prot2 | | Decl | rel: 0 -// CHECK: [[@LINE+2]]:17 | objc-protocol/ObjC | Prot1 | c:objc(pl)Prot1 | | Ref,RelBase | rel: 1 +// CHECK: [[@LINE+3]]:11 | protocol/ObjC | Prot2 | c:objc(pl)Prot2 | | Decl | rel: 0 +// CHECK: [[@LINE+2]]:17 | protocol/ObjC | Prot1 | c:objc(pl)Prot1 | | Ref,RelBase | rel: 1 // CHECK-NEXT: RelBase | Prot2 | c:objc(pl)Prot2 @protocol Prot2 @end -// CHECK: [[@LINE+7]]:12 | objc-class/ObjC | Sub | c:objc(cs)Sub | _OBJC_CLASS_$_Sub | Decl | rel: 0 -// CHECK: [[@LINE+6]]:18 | objc-class/ObjC | Base | c:objc(cs)Base | _OBJC_CLASS_$_Base | Ref,RelBase | rel: 1 +// CHECK: [[@LINE+7]]:12 | class/ObjC | Sub | c:objc(cs)Sub | _OBJC_CLASS_$_Sub | Decl | rel: 0 +// CHECK: [[@LINE+6]]:18 | class/ObjC | Base | c:objc(cs)Base | _OBJC_CLASS_$_Base | Ref,RelBase | rel: 1 // CHECK-NEXT: RelBase | Sub | c:objc(cs)Sub -// CHECK: [[@LINE+4]]:23 | objc-protocol/ObjC | Prot2 | c:objc(pl)Prot2 | | Ref,RelBase | rel: 1 +// CHECK: [[@LINE+4]]:23 | protocol/ObjC | Prot2 | c:objc(pl)Prot2 | | Ref,RelBase | rel: 1 // CHECK-NEXT: RelBase | Sub | c:objc(cs)Sub -// CHECK: [[@LINE+2]]:30 | objc-protocol/ObjC | Prot1 | c:objc(pl)Prot1 | | Ref,RelBase | rel: 1 +// CHECK: [[@LINE+2]]:30 | protocol/ObjC | Prot1 | c:objc(pl)Prot1 | | Ref,RelBase | rel: 1 // CHECK-NEXT: RelBase | Sub | c:objc(cs)Sub @interface Sub : Base @end @@ -66,8 +66,8 @@ -(ObjectType)getit; Two, }; -// CHECK: [[@LINE+1]]:13 | typedef/C | jmp_buf | c:index-source.m@T@jmp_buf | | Def | rel: 0 +// CHECK: [[@LINE+1]]:13 | type-alias/C | jmp_buf | c:index-source.m@T@jmp_buf | | Def | rel: 0 typedef int jmp_buf[(18)]; // CHECK: [[@LINE+2]]:12 | function/C | setjmp | c:@F@setjmp | _setjmp | Decl | rel: 0 -// CHECK: [[@LINE+1]]:19 | typedef/C | jmp_buf | c:index-source.m@T@jmp_buf | | Ref | rel: 0 +// CHECK: [[@LINE+1]]:19 | type-alias/C | jmp_buf | c:index-source.m@T@jmp_buf | | Ref | rel: 0 extern int setjmp(jmp_buf); diff --git a/tools/libclang/CXIndexDataConsumer.cpp b/tools/libclang/CXIndexDataConsumer.cpp index 911c79c6e23..e2edad2e1b4 100644 --- a/tools/libclang/CXIndexDataConsumer.cpp +++ b/tools/libclang/CXIndexDataConsumer.cpp @@ -1126,7 +1126,7 @@ void CXIndexDataConsumer::translateLoc(SourceLocation Loc, *offset = FileOffset; } -static CXIdxEntityKind getEntityKindFromSymbolKind(SymbolKind K); +static CXIdxEntityKind getEntityKindFromSymbolKind(SymbolKind K, SymbolLanguage L); static CXIdxEntityCXXTemplateKind getEntityKindFromSymbolCXXTemplateKind(SymbolCXXTemplateKind K); static CXIdxEntityLanguage getEntityLangFromSymbolLang(SymbolLanguage L); @@ -1143,7 +1143,7 @@ void CXIndexDataConsumer::getEntityInfo(const NamedDecl *D, EntityInfo.IndexCtx = this; SymbolInfo SymInfo = getSymbolInfo(D); - EntityInfo.kind = getEntityKindFromSymbolKind(SymInfo.Kind); + EntityInfo.kind = getEntityKindFromSymbolKind(SymInfo.Kind, SymInfo.Lang); EntityInfo.templateKind = getEntityKindFromSymbolCXXTemplateKind(SymInfo.TemplateKind); EntityInfo.lang = getEntityLangFromSymbolLang(SymInfo.Lang); @@ -1236,40 +1236,50 @@ bool CXIndexDataConsumer::isTemplateImplicitInstantiation(const Decl *D) { return false; } -static CXIdxEntityKind getEntityKindFromSymbolKind(SymbolKind K) { +static CXIdxEntityKind getEntityKindFromSymbolKind(SymbolKind K, SymbolLanguage Lang) { switch (K) { case SymbolKind::Unknown: case SymbolKind::Module: case SymbolKind::Macro: + case SymbolKind::ClassProperty: return CXIdxEntity_Unexposed; case SymbolKind::Enum: return CXIdxEntity_Enum; case SymbolKind::Struct: return CXIdxEntity_Struct; case SymbolKind::Union: return CXIdxEntity_Union; - case SymbolKind::Typedef: return CXIdxEntity_Typedef; + case SymbolKind::TypeAlias: + if (Lang == SymbolLanguage::CXX) + return CXIdxEntity_CXXTypeAlias; + return CXIdxEntity_Typedef; case SymbolKind::Function: return CXIdxEntity_Function; case SymbolKind::Variable: return CXIdxEntity_Variable; - case SymbolKind::Field: return CXIdxEntity_Field; + case SymbolKind::Field: + if (Lang == SymbolLanguage::ObjC) + return CXIdxEntity_ObjCIvar; + return CXIdxEntity_Field; case SymbolKind::EnumConstant: return CXIdxEntity_EnumConstant; - case SymbolKind::ObjCClass: return CXIdxEntity_ObjCClass; - case SymbolKind::ObjCProtocol: return CXIdxEntity_ObjCProtocol; - case SymbolKind::ObjCCategory: return CXIdxEntity_ObjCCategory; - case SymbolKind::ObjCInstanceMethod: return CXIdxEntity_ObjCInstanceMethod; - case SymbolKind::ObjCClassMethod: return CXIdxEntity_ObjCClassMethod; - case SymbolKind::ObjCProperty: return CXIdxEntity_ObjCProperty; - case SymbolKind::ObjCIvar: return CXIdxEntity_ObjCIvar; - case SymbolKind::CXXClass: return CXIdxEntity_CXXClass; - case SymbolKind::CXXNamespace: return CXIdxEntity_CXXNamespace; - case SymbolKind::CXXNamespaceAlias: return CXIdxEntity_CXXNamespaceAlias; - case SymbolKind::CXXStaticVariable: return CXIdxEntity_CXXStaticVariable; - case SymbolKind::CXXStaticMethod: return CXIdxEntity_CXXStaticMethod; - case SymbolKind::CXXInstanceMethod: return CXIdxEntity_CXXInstanceMethod; - case SymbolKind::CXXConstructor: return CXIdxEntity_CXXConstructor; - case SymbolKind::CXXDestructor: return CXIdxEntity_CXXDestructor; - case SymbolKind::CXXConversionFunction: - return CXIdxEntity_CXXConversionFunction; - case SymbolKind::CXXTypeAlias: return CXIdxEntity_CXXTypeAlias; - case SymbolKind::CXXInterface: return CXIdxEntity_CXXInterface; + case SymbolKind::Class: + if (Lang == SymbolLanguage::ObjC) + return CXIdxEntity_ObjCClass; + return CXIdxEntity_CXXClass; + case SymbolKind::Protocol: + if (Lang == SymbolLanguage::ObjC) + return CXIdxEntity_ObjCProtocol; + return CXIdxEntity_CXXInterface; + case SymbolKind::Extension: return CXIdxEntity_ObjCCategory; + case SymbolKind::InstanceMethod: + if (Lang == SymbolLanguage::ObjC) + return CXIdxEntity_ObjCInstanceMethod; + return CXIdxEntity_CXXInstanceMethod; + case SymbolKind::ClassMethod: return CXIdxEntity_ObjCClassMethod; + case SymbolKind::StaticMethod: return CXIdxEntity_CXXStaticMethod; + case SymbolKind::InstanceProperty: return CXIdxEntity_ObjCProperty; + case SymbolKind::StaticProperty: return CXIdxEntity_CXXStaticVariable; + case SymbolKind::Namespace: return CXIdxEntity_CXXNamespace; + case SymbolKind::NamespaceAlias: return CXIdxEntity_CXXNamespaceAlias; + case SymbolKind::Constructor: return CXIdxEntity_CXXConstructor; + case SymbolKind::Destructor: return CXIdxEntity_CXXDestructor; + case SymbolKind::ConversionFunction: return CXIdxEntity_CXXConversionFunction; } } From c02ce6789995e746d180102b8f084d019ad4418a Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes Date: Thu, 31 Mar 2016 02:45:46 +0000 Subject: [PATCH 385/742] [DarwinDriver] Increase the number of valid digits for ld64 version string. Previously only 3 digits were valid. Increase it to 5. Differential Revision: http://reviews.llvm.org/D18304 rdar://problem/24843016 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@264987 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Driver/Driver.h | 9 +++++++++ lib/Driver/Driver.cpp | 28 ++++++++++++++++++++++++++++ lib/Driver/Tools.cpp | 7 ++----- test/Driver/darwin-ld.c | 24 ++++++++++++++++++++++++ 4 files changed, 63 insertions(+), 5 deletions(-) diff --git a/include/clang/Driver/Driver.h b/include/clang/Driver/Driver.h index 250211c9cff..3f6e00681b7 100644 --- a/include/clang/Driver/Driver.h +++ b/include/clang/Driver/Driver.h @@ -466,6 +466,15 @@ class Driver { static bool GetReleaseVersion(const char *Str, unsigned &Major, unsigned &Minor, unsigned &Micro, bool &HadExtra); + + /// Parse digits from a string \p Str and fulfill \p Digits with + /// the parsed numbers. This method assumes that the max number of + /// digits to look for is equal to Digits.size(). + /// + /// \return True if the entire string was parsed and there are + /// no extra characters remaining at the end. + static bool GetReleaseVersion(const char *Str, + MutableArrayRef Digits); }; /// \return True if the last defined optimization level is -Ofast. diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 5c01ef0bba7..eafc0afff54 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -2412,6 +2412,34 @@ bool Driver::GetReleaseVersion(const char *Str, unsigned &Major, return true; } +/// Parse digits from a string \p Str and fulfill \p Digits with +/// the parsed numbers. This method assumes that the max number of +/// digits to look for is equal to Digits.size(). +/// +/// \return True if the entire string was parsed and there are +/// no extra characters remaining at the end. +bool Driver::GetReleaseVersion(const char *Str, + MutableArrayRef Digits) { + if (*Str == '\0') + return false; + + char *End; + unsigned CurDigit = 0; + while (CurDigit < Digits.size()) { + unsigned Digit = (unsigned)strtol(Str, &End, 10); + Digits[CurDigit] = Digit; + if (*Str != '\0' && *End == '\0') + return true; + if (*End != '.' || Str == End) + return false; + Str = End + 1; + CurDigit++; + } + + // More digits than requested, bail out... + return false; +} + std::pair Driver::getIncludeExcludeOptionFlagMasks() const { unsigned IncludedFlagsBitmask = 0; unsigned ExcludedFlagsBitmask = options::NoDriverOption; diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index 22a50b290eb..a3eeb9563f8 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -7064,12 +7064,9 @@ void darwin::Linker::AddLinkArgs(Compilation &C, const ArgList &Args, const Driver &D = getToolChain().getDriver(); const toolchains::MachO &MachOTC = getMachOToolChain(); - unsigned Version[3] = {0, 0, 0}; + unsigned Version[5] = {0, 0, 0, 0, 0}; if (Arg *A = Args.getLastArg(options::OPT_mlinker_version_EQ)) { - bool HadExtra; - if (!Driver::GetReleaseVersion(A->getValue(), Version[0], Version[1], - Version[2], HadExtra) || - HadExtra) + if (!Driver::GetReleaseVersion(A->getValue(), Version)) D.Diag(diag::err_drv_invalid_version_number) << A->getAsString(Args); } diff --git a/test/Driver/darwin-ld.c b/test/Driver/darwin-ld.c index ffef3b14785..66c5206b4c8 100644 --- a/test/Driver/darwin-ld.c +++ b/test/Driver/darwin-ld.c @@ -294,3 +294,27 @@ // RUN: FileCheck --check-prefix=LINK-IFRAMEWORK %s // LINK-IFRAMEWORK: {{ld(.exe)?"}} // LINK-IFRAMEWORK: "-FBar" + +// Check ld64 accepts up to 5 digits with no extra characters +// RUN: %clang -target x86_64-apple-darwin12 %s -### -o %t \ +// RUN: -mlinker-version=133.3 2> %t.log +// RUN: %clang -target x86_64-apple-darwin12 %s -### -o %t \ +// RUN: -mlinker-version=133.3.0 2>> %t.log +// RUN: %clang -target x86_64-apple-darwin12 %s -### -o %t \ +// RUN: -mlinker-version=133.3.0.1 2>> %t.log +// RUN: %clang -target x86_64-apple-darwin12 %s -### -o %t \ +// RUN: -mlinker-version=133.3.0.1.2 2>> %t.log +// RUN: %clang -target x86_64-apple-darwin12 %s -### -o %t \ +// RUN: -mlinker-version=133.3.0.1.2.6 2>> %t.log +// RUN: %clang -target x86_64-apple-darwin12 %s -### -o %t \ +// RUN: -mlinker-version=133.3.0.1.a 2>> %t.log +// RUN: %clang -target x86_64-apple-darwin12 %s -### -o %t \ +// RUN: -mlinker-version=133.3.0.1a 2>> %t.log +// RUN: FileCheck -check-prefix=LINK_VERSION_DIGITS %s < %t.log +// LINK_VERSION_DIGITS-NOT: invalid version number in '-mlinker-version=133.3' +// LINK_VERSION_DIGITS-NOT: invalid version number in '-mlinker-version=133.3.0' +// LINK_VERSION_DIGITS-NOT: invalid version number in '-mlinker-version=133.3.0.1' +// LINK_VERSION_DIGITS-NOT: invalid version number in '-mlinker-version=133.3.0.1.2' +// LINK_VERSION_DIGITS: invalid version number in '-mlinker-version=133.3.0.1.2.6' +// LINK_VERSION_DIGITS: invalid version number in '-mlinker-version=133.3.0.1.a' +// LINK_VERSION_DIGITS: invalid version number in '-mlinker-version=133.3.0.1a' From 67717ad22d79ae63b9371feab41efe6264780d1e Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Thu, 10 Mar 2016 23:51:03 +0000 Subject: [PATCH 386/742] Add has_feature objc_class_property. rdar://23891898 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263171 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit a118d7d4a2e0814ae0711ac47253ec8d6b264855) --- lib/Lex/PPMacroExpansion.cpp | 1 + test/SemaObjC/objc-class-property.m | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp index c11e37f5a9d..1be894d9229 100644 --- a/lib/Lex/PPMacroExpansion.cpp +++ b/lib/Lex/PPMacroExpansion.cpp @@ -1116,6 +1116,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .Case("objc_bridge_id_on_typedefs", true) .Case("objc_generics", LangOpts.ObjC2) .Case("objc_generics_variance", LangOpts.ObjC2) + .Case("objc_class_property", LangOpts.ObjC2) // C11 features .Case("c_alignas", LangOpts.C11) .Case("c_alignof", LangOpts.C11) diff --git a/test/SemaObjC/objc-class-property.m b/test/SemaObjC/objc-class-property.m index 77754400905..0058ee3648b 100644 --- a/test/SemaObjC/objc-class-property.m +++ b/test/SemaObjC/objc-class-property.m @@ -1,5 +1,9 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s +#if !__has_feature(objc_class_property) +#error does not support class property +#endif + @interface Root -(id) alloc; -(id) init; From be50618180fd6094b5194cb6c4b81fa9fadeb08c Mon Sep 17 00:00:00 2001 From: Roman Levenstein Date: Wed, 16 Mar 2016 18:00:46 +0000 Subject: [PATCH 387/742] Add attributes for preserve_mostcc/preserve_allcc calling conventions to the C/C++ front-end Till now, preserve_mostcc/preserve_allcc calling convention attributes were only available at the LLVM IR level. This patch adds attributes for preserve_mostcc/preserve_allcc calling conventions to the C/C++ front-end. The code was mostly written by Juergen Ributzka. I just added support for the AArch64 target and tests. Differential Revision: http://reviews.llvm.org/D18025 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263647 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 5e8bd88711d23b2a5db79f43580afebdfd10a394) Conflicts: include/clang-c/Index.h include/clang/Basic/Specifiers.h lib/AST/ItaniumMangle.cpp lib/AST/Type.cpp lib/AST/TypePrinter.cpp lib/Basic/Targets.cpp tools/libclang/CXType.cpp --- include/clang-c/Index.h | 2 + include/clang/AST/Type.h | 2 + include/clang/Basic/Attr.td | 10 +++++ include/clang/Basic/AttrDocs.td | 67 +++++++++++++++++++++++++++++++ include/clang/Basic/Specifiers.h | 2 + lib/AST/Type.cpp | 6 +++ lib/AST/TypePrinter.cpp | 12 ++++++ lib/Basic/Targets.cpp | 26 ++++++++++-- lib/CodeGen/CGCall.cpp | 8 ++++ lib/Sema/SemaDeclAttr.cpp | 13 +++++- lib/Sema/SemaType.cpp | 12 +++++- test/CodeGen/preserve-call-conv.c | 17 ++++++++ test/Sema/preserve-call-conv.c | 35 ++++++++++++++++ tools/libclang/CXType.cpp | 2 + 14 files changed, 208 insertions(+), 6 deletions(-) create mode 100644 test/CodeGen/preserve-call-conv.c create mode 100644 test/Sema/preserve-call-conv.c diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h index 4ecbed5f19a..cbb5052ecfc 100644 --- a/include/clang-c/Index.h +++ b/include/clang-c/Index.h @@ -2943,6 +2943,8 @@ enum CXCallingConv { CXCallingConv_X86_64Win64 = 10, CXCallingConv_X86_64SysV = 11, CXCallingConv_X86VectorCall = 12, + CXCallingConv_PreserveMost = 14, + CXCallingConv_PreserveAll = 15, CXCallingConv_Invalid = 100, CXCallingConv_Unexposed = 200 diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index d63b2c43d55..2929037a87c 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -3630,6 +3630,8 @@ class AttributedType : public Type, public llvm::FoldingSetNode { attr_inteloclbicc, attr_ms_abi, attr_sysv_abi, + attr_preserve_most, + attr_preserve_all, attr_ptr32, attr_ptr64, attr_sptr, diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index a3640876fdb..4e1d30afa97 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -1426,6 +1426,16 @@ def Pascal : InheritableAttr { let Documentation = [Undocumented]; } +def PreserveMost : InheritableAttr { + let Spellings = [GNU<"preserve_most">]; + let Documentation = [PreserveMostDocs]; +} + +def PreserveAll : InheritableAttr { + let Spellings = [GNU<"preserve_all">]; + let Documentation = [PreserveAllDocs]; +} + def Target : InheritableAttr { let Spellings = [GCC<"target">]; let Args = [StringArgument<"featuresStr">]; diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td index 568cc83af57..45edb850160 100644 --- a/include/clang/Basic/AttrDocs.td +++ b/include/clang/Basic/AttrDocs.td @@ -1987,3 +1987,70 @@ to replace the deprecated name with a new name. Otherwise, when spelled as string argument which is the message to display when emitting the warning. }]; } + +def PreserveMostDocs : Documentation { + let Category = DocCatCallingConvs; + let Content = [{ +On X86-64 and AArch64 targets, this attribute changes the calling convention of +a function. The ``preserve_most`` calling convention attempts to make the code +in the caller as unintrusive as possible. This convention behaves identically +to the ``C`` calling convention on how arguments and return values are passed, +but it uses a different set of caller/callee-saved registers. This alleviates +the burden of saving and recovering a large register set before and after the +call in the caller. If the arguments are passed in callee-saved registers, +then they will be preserved by the callee across the call. This doesn't +apply for values returned in callee-saved registers. + +- On X86-64 the callee preserves all general purpose registers, except for + R11. R11 can be used as a scratch register. Floating-point registers + (XMMs/YMMs) are not preserved and need to be saved by the caller. + +The idea behind this convention is to support calls to runtime functions +that have a hot path and a cold path. The hot path is usually a small piece +of code that doesn't use many registers. The cold path might need to call out to +another function and therefore only needs to preserve the caller-saved +registers, which haven't already been saved by the caller. The +`preserve_most` calling convention is very similar to the ``cold`` calling +convention in terms of caller/callee-saved registers, but they are used for +different types of function calls. ``coldcc`` is for function calls that are +rarely executed, whereas `preserve_most` function calls are intended to be +on the hot path and definitely executed a lot. Furthermore ``preserve_most`` +doesn't prevent the inliner from inlining the function call. + +This calling convention will be used by a future version of the Objective-C +runtime and should therefore still be considered experimental at this time. +Although this convention was created to optimize certain runtime calls to +the Objective-C runtime, it is not limited to this runtime and might be used +by other runtimes in the future too. The current implementation only +supports X86-64 and AArch64, but the intention is to support more architectures +in the future. + }]; +} + +def PreserveAllDocs : Documentation { + let Category = DocCatCallingConvs; + let Content = [{ +On X86-64 and AArch64 targets, this attribute changes the calling convention of +a function. The ``preserve_all`` calling convention attempts to make the code +in the caller even less intrusive than the ``preserve_most`` calling convention. +This calling convention also behaves identical to the ``C`` calling convention +on how arguments and return values are passed, but it uses a different set of +caller/callee-saved registers. This removes the burden of saving and +recovering a large register set before and after the call in the caller. If +the arguments are passed in callee-saved registers, then they will be +preserved by the callee across the call. This doesn't apply for values +returned in callee-saved registers. + +- On X86-64 the callee preserves all general purpose registers, except for + R11. R11 can be used as a scratch register. Furthermore it also preserves + all floating-point registers (XMMs/YMMs). + +The idea behind this convention is to support calls to runtime functions +that don't need to call out to any other functions. + +This calling convention, like the ``preserve_most`` calling convention, will be +used by a future version of the Objective-C runtime and should be considered +experimental at this time. + }]; +} + diff --git a/include/clang/Basic/Specifiers.h b/include/clang/Basic/Specifiers.h index e284171f777..ff95ba1d26f 100644 --- a/include/clang/Basic/Specifiers.h +++ b/include/clang/Basic/Specifiers.h @@ -239,6 +239,8 @@ namespace clang { CC_IntelOclBicc, // __attribute__((intel_ocl_bicc)) CC_SpirFunction, // default for OpenCL functions on SPIR target CC_SpirKernel // inferred for OpenCL kernels on SPIR target + CC_PreserveMost, // __attribute__((preserve_most)) + CC_PreserveAll, // __attribute__((preserve_all)) }; /// \brief Checks whether the given calling convention supports variadic diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index b467dac66b5..721b8b21a73 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -2655,6 +2655,8 @@ StringRef FunctionType::getNameForCallConv(CallingConv CC) { case CC_IntelOclBicc: return "intel_ocl_bicc"; case CC_SpirFunction: return "spir_function"; case CC_SpirKernel: return "spir_kernel"; + case CC_PreserveMost: return "preserve_most"; + case CC_PreserveAll: return "preserve_all"; } llvm_unreachable("Invalid calling convention."); @@ -2996,6 +2998,8 @@ bool AttributedType::isQualifier() const { case AttributedType::attr_pascal: case AttributedType::attr_vectorcall: case AttributedType::attr_inteloclbicc: + case AttributedType::attr_preserve_most: + case AttributedType::attr_preserve_all: case AttributedType::attr_ms_abi: case AttributedType::attr_sysv_abi: case AttributedType::attr_ptr32: @@ -3052,6 +3056,8 @@ bool AttributedType::isCallingConv() const { case attr_ms_abi: case attr_sysv_abi: case attr_inteloclbicc: + case attr_preserve_most: + case attr_preserve_all: return true; } llvm_unreachable("invalid attr kind"); diff --git a/lib/AST/TypePrinter.cpp b/lib/AST/TypePrinter.cpp index b202523bdaf..762033b600e 100644 --- a/lib/AST/TypePrinter.cpp +++ b/lib/AST/TypePrinter.cpp @@ -703,6 +703,12 @@ void TypePrinter::printFunctionProtoAfter(const FunctionProtoType *T, case CC_SpirKernel: // Do nothing. These CCs are not available as attributes. break; + case CC_PreserveMost: + OS << " __attribute__((preserve_most))"; + break; + case CC_PreserveAll: + OS << " __attribute__((preserve_all))"; + break; } } @@ -1321,6 +1327,12 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, break; } case AttributedType::attr_inteloclbicc: OS << "inteloclbicc"; break; + case AttributedType::attr_preserve_most: + OS << "preserve_most"; + break; + case AttributedType::attr_preserve_all: + OS << "preserve_all"; + break; } OS << "))"; } diff --git a/lib/Basic/Targets.cpp b/lib/Basic/Targets.cpp index d75aa3e0095..3eacaf0d5ce 100644 --- a/lib/Basic/Targets.cpp +++ b/lib/Basic/Targets.cpp @@ -4017,10 +4017,17 @@ class X86_64TargetInfo : public X86TargetInfo { } CallingConvCheckResult checkCallingConvention(CallingConv CC) const override { - return (CC == CC_C || - CC == CC_X86VectorCall || - CC == CC_IntelOclBicc || - CC == CC_X86_64Win64) ? CCCR_OK : CCCR_Warning; + switch (CC) { + case CC_C: + case CC_X86VectorCall: + case CC_IntelOclBicc: + case CC_X86_64Win64: + case CC_PreserveMost: + case CC_PreserveAll: + return CCCR_OK; + default: + return CCCR_Warning; + } } CallingConv getDefaultCallingConv(CallingConvMethodType MT) const override { @@ -5448,6 +5455,17 @@ class AArch64TargetInfo : public TargetInfo { return true; } + CallingConvCheckResult checkCallingConvention(CallingConv CC) const override { + switch (CC) { + case CC_C: + case CC_PreserveMost: + case CC_PreserveAll: + return CCCR_OK; + default: + return CCCR_Warning; + } + } + bool isCLZForZeroUndef() const override { return false; } BuiltinVaListKind getBuiltinVaListKind() const override { diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index 1fcb934f05e..35b0585069b 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -57,6 +57,8 @@ static unsigned ClangCallConvToLLVMCallConv(CallingConv CC) { case CC_X86VectorCall: return llvm::CallingConv::X86_VectorCall; case CC_SpirFunction: return llvm::CallingConv::SPIR_FUNC; case CC_SpirKernel: return llvm::CallingConv::SPIR_KERNEL; + case CC_PreserveMost: return llvm::CallingConv::PreserveMost; + case CC_PreserveAll: return llvm::CallingConv::PreserveAll; } } @@ -174,6 +176,12 @@ static CallingConv getCallingConventionForDecl(const Decl *D, bool IsWindows) { if (D->hasAttr()) return IsWindows ? CC_X86_64SysV : CC_C; + if (D->hasAttr()) + return CC_PreserveMost; + + if (D->hasAttr()) + return CC_PreserveAll; + return CC_C; } diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index c079984c2ce..fdf52d76f9d 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -3773,7 +3773,14 @@ static void handleCallConvAttr(Sema &S, Decl *D, const AttributeList &Attr) { IntelOclBiccAttr(Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); return; - + case AttributeList::AT_PreserveMost: + D->addAttr(::new (S.Context) PreserveMostAttr( + Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); + return; + case AttributeList::AT_PreserveAll: + D->addAttr(::new (S.Context) PreserveAllAttr( + Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); + return; default: llvm_unreachable("unexpected attribute kind"); } @@ -3825,6 +3832,8 @@ bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC, return true; } case AttributeList::AT_IntelOclBicc: CC = CC_IntelOclBicc; break; + case AttributeList::AT_PreserveMost: CC = CC_PreserveMost; break; + case AttributeList::AT_PreserveAll: CC = CC_PreserveAll; break; default: llvm_unreachable("unexpected attribute kind"); } @@ -5812,6 +5821,8 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_SysVABI: case AttributeList::AT_Pcs: case AttributeList::AT_IntelOclBicc: + case AttributeList::AT_PreserveMost: + case AttributeList::AT_PreserveAll: handleCallConvAttr(S, D, Attr); break; case AttributeList::AT_OpenCLKernel: diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 45564fe6b8c..cf063669ec8 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -113,7 +113,9 @@ static void diagnoseBadTypeAttribute(Sema &S, const AttributeList &attr, case AttributeList::AT_SysVABI: \ case AttributeList::AT_Regparm: \ case AttributeList::AT_Pcs: \ - case AttributeList::AT_IntelOclBicc + case AttributeList::AT_IntelOclBicc: \ + case AttributeList::AT_PreserveMost: \ + case AttributeList::AT_PreserveAll // Microsoft-specific type qualifiers. #define MS_TYPE_ATTRS_CASELIST \ @@ -4518,6 +4520,10 @@ static AttributeList::Kind getAttrListKind(AttributedType::Kind kind) { return AttributeList::AT_MSABI; case AttributedType::attr_sysv_abi: return AttributeList::AT_SysVABI; + case AttributedType::attr_preserve_most: + return AttributeList::AT_PreserveMost; + case AttributedType::attr_preserve_all: + return AttributeList::AT_PreserveAll; case AttributedType::attr_ptr32: return AttributeList::AT_Ptr32; case AttributedType::attr_ptr64: @@ -5853,6 +5859,10 @@ static AttributedType::Kind getCCTypeAttrKind(AttributeList &Attr) { return AttributedType::attr_ms_abi; case AttributeList::AT_SysVABI: return AttributedType::attr_sysv_abi; + case AttributeList::AT_PreserveMost: + return AttributedType::attr_preserve_most; + case AttributeList::AT_PreserveAll: + return AttributedType::attr_preserve_all; } llvm_unreachable("unexpected attribute kind!"); } diff --git a/test/CodeGen/preserve-call-conv.c b/test/CodeGen/preserve-call-conv.c new file mode 100644 index 00000000000..6e91a8489b4 --- /dev/null +++ b/test/CodeGen/preserve-call-conv.c @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm < %s | FileCheck %s +// RUN: %clang_cc1 -triple arm64-unknown-unknown -emit-llvm < %s | FileCheck %s + +// Check that the preserve_most calling convention attribute at the source level +// is lowered to the corresponding calling convention attrribute at the LLVM IR +// level. +void foo() __attribute__((preserve_most)) { + // CHECK-LABEL: define preserve_mostcc void @foo() +} + +// Check that the preserve_most calling convention attribute at the source level +// is lowered to the corresponding calling convention attrribute at the LLVM IR +// level. +void boo() __attribute__((preserve_all)) { + // CHECK-LABEL: define preserve_allcc void @boo() +} + diff --git a/test/Sema/preserve-call-conv.c b/test/Sema/preserve-call-conv.c new file mode 100644 index 00000000000..f258f45ac58 --- /dev/null +++ b/test/Sema/preserve-call-conv.c @@ -0,0 +1,35 @@ +// RUN: %clang_cc1 %s -fsyntax-only -triple x86_64-unknown-unknown -verify +// RUN: %clang_cc1 %s -fsyntax-only -triple arm64-unknown-unknown -verify +typedef void typedef_fun_t(int); + +void __attribute__((preserve_most)) foo(void *ptr) { +} + +void __attribute__((preserve_most(1))) foo1(void *ptr) { // expected-error {{'preserve_most' attribute takes no arguments}} +} + +void (__attribute__((preserve_most)) *pfoo1)(void *) = foo; + +void (__attribute__((cdecl)) *pfoo2)(void *) = foo; // expected-warning {{incompatible pointer types initializing 'void (*)(void *) __attribute__((cdecl))' with an expression of type 'void (void *) __attribute__((preserve_most))'}} +void (*pfoo3)(void *) = foo; // expected-warning {{incompatible pointer types initializing 'void (*)(void *)' with an expression of type 'void (void *) __attribute__((preserve_most))'}} + +typedef_fun_t typedef_fun_foo; // expected-note {{previous declaration is here}} +void __attribute__((preserve_most)) typedef_fun_foo(int x) { } // expected-error {{function declared 'preserve_most' here was previously declared without calling convention}} + +struct type_test_foo {} __attribute__((preserve_most)); // expected-warning {{'preserve_most' attribute only applies to functions and methods}} + +void __attribute__((preserve_all)) boo(void *ptr) { +} + +void __attribute__((preserve_all(1))) boo1(void *ptr) { // expected-error {{'preserve_all' attribute takes no arguments}} +} + +void (__attribute__((preserve_all)) *pboo1)(void *) = boo; + +void (__attribute__((cdecl)) *pboo2)(void *) = boo; // expected-warning {{incompatible pointer types initializing 'void (*)(void *) __attribute__((cdecl))' with an expression of type 'void (void *) __attribute__((preserve_all))'}} +void (*pboo3)(void *) = boo; // expected-warning {{incompatible pointer types initializing 'void (*)(void *)' with an expression of type 'void (void *) __attribute__((preserve_all))'}} + +typedef_fun_t typedef_fun_boo; // expected-note {{previous declaration is here}} +void __attribute__((preserve_all)) typedef_fun_boo(int x) { } // expected-error {{function declared 'preserve_all' here was previously declared without calling convention}} + +struct type_test_boo {} __attribute__((preserve_all)); // expected-warning {{'preserve_all' attribute only applies to functions and methods}} diff --git a/tools/libclang/CXType.cpp b/tools/libclang/CXType.cpp index 44bb631f786..d6d5daa0f9d 100644 --- a/tools/libclang/CXType.cpp +++ b/tools/libclang/CXType.cpp @@ -533,6 +533,8 @@ CXCallingConv clang_getFunctionTypeCallingConv(CXType X) { TCALLINGCONV(AAPCS); TCALLINGCONV(AAPCS_VFP); TCALLINGCONV(IntelOclBicc); + TCALLINGCONV(PreserveMost); + TCALLINGCONV(PreserveAll); case CC_SpirFunction: return CXCallingConv_Unexposed; case CC_SpirKernel: return CXCallingConv_Unexposed; break; From 7af77768488948c942ac75c6eb0ad8d3d7cd177b Mon Sep 17 00:00:00 2001 From: Tim Northover Date: Thu, 31 Mar 2016 19:19:24 +0000 Subject: [PATCH 388/742] Diagnostics: remove dodgy handler for bitcode inlineasm diagnostics. Whatever crash it was there to present appears to have been fixed in the backend now, and it had the nasty side-effect of causing clang to exit(0) and leave a .o containing goodness knows what even when an error hit. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265038 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CodeGenAction.cpp | 8 -------- test/CodeGen/asm-errors.c | 2 +- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/lib/CodeGen/CodeGenAction.cpp b/lib/CodeGen/CodeGenAction.cpp index cdd9d09d048..1b4ed23f974 100644 --- a/lib/CodeGen/CodeGenAction.cpp +++ b/lib/CodeGen/CodeGenAction.cpp @@ -725,12 +725,6 @@ CodeGenAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { return std::move(Result); } -static void BitcodeInlineAsmDiagHandler(const llvm::SMDiagnostic &SM, - void *Context, - unsigned LocCookie) { - SM.print(nullptr, llvm::errs()); -} - void CodeGenAction::ExecuteAction() { // If this is an IR file, we have to treat it specially. if (getCurrentFileKind() == IK_LLVM_IR) { @@ -779,8 +773,6 @@ void CodeGenAction::ExecuteAction() { TheModule->setTargetTriple(TargetOpts.Triple); } - LLVMContext &Ctx = TheModule->getContext(); - Ctx.setInlineAsmDiagnosticHandler(BitcodeInlineAsmDiagHandler); EmitBackendOutput(CI.getDiagnostics(), CI.getCodeGenOpts(), TargetOpts, CI.getLangOpts(), CI.getTarget().getDataLayoutString(), TheModule.get(), BA, OS); diff --git a/test/CodeGen/asm-errors.c b/test/CodeGen/asm-errors.c index 05751a8e690..9acb693d581 100644 --- a/test/CodeGen/asm-errors.c +++ b/test/CodeGen/asm-errors.c @@ -4,7 +4,7 @@ // UN: not %clang_cc1 -triple i386-apple-darwin10 -emit-obj %s -o /dev/null > %t 2>&1 // UN: FileCheck %s < %t // RUN: %clang_cc1 -triple i386-apple-darwin10 -emit-llvm-bc %s -o %t.bc -// RUN: %clang_cc1 -triple i386-apple-darwin10 -emit-obj %t.bc -o /dev/null 2>&1 | \ +// RUN: not %clang_cc1 -triple i386-apple-darwin10 -emit-obj %t.bc -o /dev/null 2>&1 | \ // RUN: FileCheck --check-prefix=CRASH-REPORT %s // CRASH-REPORT: : // CRASH-REPORT: error: invalid instruction mnemonic 'abc' From 93fa82e8b4d48efd87b95b3287b8a9ecb3135b2f Mon Sep 17 00:00:00 2001 From: Tim Northover Date: Thu, 31 Mar 2016 12:34:33 -0700 Subject: [PATCH 389/742] Fix weird syntax error. --- include/clang/Basic/Specifiers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/clang/Basic/Specifiers.h b/include/clang/Basic/Specifiers.h index ff95ba1d26f..c9529eb223f 100644 --- a/include/clang/Basic/Specifiers.h +++ b/include/clang/Basic/Specifiers.h @@ -238,7 +238,7 @@ namespace clang { CC_AAPCS_VFP, // __attribute__((pcs("aapcs-vfp"))) CC_IntelOclBicc, // __attribute__((intel_ocl_bicc)) CC_SpirFunction, // default for OpenCL functions on SPIR target - CC_SpirKernel // inferred for OpenCL kernels on SPIR target + CC_SpirKernel, // inferred for OpenCL kernels on SPIR target CC_PreserveMost, // __attribute__((preserve_most)) CC_PreserveAll, // __attribute__((preserve_all)) }; From 68d1e9985025d506c88b3c220e10fe18697eead9 Mon Sep 17 00:00:00 2001 From: Ben Langmuir Date: Fri, 25 Mar 2016 17:01:59 +0000 Subject: [PATCH 390/742] [index] Remove redundancy between symbol kind and language Condense the ObjCKIND and CXXKIND options into just KIND, since the language was already specified on a per-symbol basis and this information was redundant. This only changes the internal representation; naturally the libclang interface remains the same. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@264423 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Index/IndexSymbol.h | 37 +++++------ lib/Index/IndexSymbol.cpp | 89 +++++++++++++------------- test/Index/Core/index-source.cpp | 2 +- test/Index/Core/index-source.m | 24 +++---- tools/libclang/CXIndexDataConsumer.cpp | 58 ++++++++++------- 5 files changed, 107 insertions(+), 103 deletions(-) diff --git a/include/clang/Index/IndexSymbol.h b/include/clang/Index/IndexSymbol.h index 484edda696f..56b12fa6bec 100644 --- a/include/clang/Index/IndexSymbol.h +++ b/include/clang/Index/IndexSymbol.h @@ -24,38 +24,33 @@ enum class SymbolKind : uint8_t { Unknown, Module, + Namespace, + NamespaceAlias, Macro, Enum, Struct, + Class, + Protocol, + Extension, Union, - Typedef, + TypeAlias, Function, Variable, Field, EnumConstant, - ObjCClass, - ObjCProtocol, - ObjCCategory, - - ObjCInstanceMethod, - ObjCClassMethod, - ObjCProperty, - ObjCIvar, - - CXXClass, - CXXNamespace, - CXXNamespaceAlias, - CXXStaticVariable, - CXXStaticMethod, - CXXInstanceMethod, - CXXConstructor, - CXXDestructor, - CXXConversionFunction, - CXXTypeAlias, - CXXInterface, + InstanceMethod, + ClassMethod, + StaticMethod, + InstanceProperty, + ClassProperty, + StaticProperty, + + Constructor, + Destructor, + ConversionFunction, }; enum class SymbolLanguage { diff --git a/lib/Index/IndexSymbol.cpp b/lib/Index/IndexSymbol.cpp index 62e2facde19..9f91e6274d4 100644 --- a/lib/Index/IndexSymbol.cpp +++ b/lib/Index/IndexSymbol.cpp @@ -30,11 +30,11 @@ SymbolInfo index::getSymbolInfo(const Decl *D) { case TTK_Union: Info.Kind = SymbolKind::Union; break; case TTK_Class: - Info.Kind = SymbolKind::CXXClass; + Info.Kind = SymbolKind::Class; Info.Lang = SymbolLanguage::CXX; break; case TTK_Interface: - Info.Kind = SymbolKind::CXXInterface; + Info.Kind = SymbolKind::Protocol; Info.Lang = SymbolLanguage::CXX; break; case TTK_Enum: @@ -57,7 +57,7 @@ SymbolInfo index::getSymbolInfo(const Decl *D) { Info.Kind = SymbolKind::Module; break; case Decl::Typedef: - Info.Kind = SymbolKind::Typedef; break; + Info.Kind = SymbolKind::TypeAlias; break; // Lang = C case Decl::Function: Info.Kind = SymbolKind::Function; break; @@ -67,7 +67,7 @@ SymbolInfo index::getSymbolInfo(const Decl *D) { case Decl::Var: Info.Kind = SymbolKind::Variable; if (isa(D->getDeclContext())) { - Info.Kind = SymbolKind::CXXStaticVariable; + Info.Kind = SymbolKind::StaticProperty; Info.Lang = SymbolLanguage::CXX; } break; @@ -83,91 +83,94 @@ SymbolInfo index::getSymbolInfo(const Decl *D) { Info.Kind = SymbolKind::EnumConstant; break; case Decl::ObjCInterface: case Decl::ObjCImplementation: - Info.Kind = SymbolKind::ObjCClass; + Info.Kind = SymbolKind::Class; Info.Lang = SymbolLanguage::ObjC; break; case Decl::ObjCProtocol: - Info.Kind = SymbolKind::ObjCProtocol; + Info.Kind = SymbolKind::Protocol; Info.Lang = SymbolLanguage::ObjC; break; case Decl::ObjCCategory: case Decl::ObjCCategoryImpl: - Info.Kind = SymbolKind::ObjCCategory; + Info.Kind = SymbolKind::Extension; Info.Lang = SymbolLanguage::ObjC; break; case Decl::ObjCMethod: if (cast(D)->isInstanceMethod()) - Info.Kind = SymbolKind::ObjCInstanceMethod; + Info.Kind = SymbolKind::InstanceMethod; else - Info.Kind = SymbolKind::ObjCClassMethod; + Info.Kind = SymbolKind::ClassMethod; Info.Lang = SymbolLanguage::ObjC; break; case Decl::ObjCProperty: - Info.Kind = SymbolKind::ObjCProperty; + Info.Kind = SymbolKind::InstanceProperty; Info.Lang = SymbolLanguage::ObjC; break; case Decl::ObjCIvar: - Info.Kind = SymbolKind::ObjCIvar; + Info.Kind = SymbolKind::Field; Info.Lang = SymbolLanguage::ObjC; break; case Decl::Namespace: - Info.Kind = SymbolKind::CXXNamespace; + Info.Kind = SymbolKind::Namespace; Info.Lang = SymbolLanguage::CXX; break; case Decl::NamespaceAlias: - Info.Kind = SymbolKind::CXXNamespaceAlias; + Info.Kind = SymbolKind::NamespaceAlias; Info.Lang = SymbolLanguage::CXX; break; case Decl::CXXConstructor: - Info.Kind = SymbolKind::CXXConstructor; + Info.Kind = SymbolKind::Constructor; Info.Lang = SymbolLanguage::CXX; break; case Decl::CXXDestructor: - Info.Kind = SymbolKind::CXXDestructor; + Info.Kind = SymbolKind::Destructor; Info.Lang = SymbolLanguage::CXX; break; case Decl::CXXConversion: - Info.Kind = SymbolKind::CXXConversionFunction; + Info.Kind = SymbolKind::ConversionFunction; Info.Lang = SymbolLanguage::CXX; break; case Decl::CXXMethod: { const CXXMethodDecl *MD = cast(D); if (MD->isStatic()) - Info.Kind = SymbolKind::CXXStaticMethod; + Info.Kind = SymbolKind::StaticMethod; else - Info.Kind = SymbolKind::CXXInstanceMethod; + Info.Kind = SymbolKind::InstanceMethod; Info.Lang = SymbolLanguage::CXX; break; } case Decl::ClassTemplate: - Info.Kind = SymbolKind::CXXClass; + Info.Kind = SymbolKind::Class; Info.TemplateKind = SymbolCXXTemplateKind::Template; + Info.Lang = SymbolLanguage::CXX; break; case Decl::FunctionTemplate: Info.Kind = SymbolKind::Function; Info.TemplateKind = SymbolCXXTemplateKind::Template; + Info.Lang = SymbolLanguage::CXX; if (const CXXMethodDecl *MD = dyn_cast_or_null( cast(D)->getTemplatedDecl())) { if (isa(MD)) - Info.Kind = SymbolKind::CXXConstructor; + Info.Kind = SymbolKind::Constructor; else if (isa(MD)) - Info.Kind = SymbolKind::CXXDestructor; + Info.Kind = SymbolKind::Destructor; else if (isa(MD)) - Info.Kind = SymbolKind::CXXConversionFunction; + Info.Kind = SymbolKind::ConversionFunction; else { if (MD->isStatic()) - Info.Kind = SymbolKind::CXXStaticMethod; + Info.Kind = SymbolKind::StaticMethod; else - Info.Kind = SymbolKind::CXXInstanceMethod; + Info.Kind = SymbolKind::InstanceMethod; } } break; case Decl::TypeAliasTemplate: - Info.Kind = SymbolKind::CXXTypeAlias; + Info.Kind = SymbolKind::TypeAlias; + Info.Lang = SymbolLanguage::CXX; Info.TemplateKind = SymbolCXXTemplateKind::Template; break; case Decl::TypeAlias: - Info.Kind = SymbolKind::CXXTypeAlias; + Info.Kind = SymbolKind::TypeAlias; Info.Lang = SymbolLanguage::CXX; break; default: @@ -262,33 +265,29 @@ StringRef index::getSymbolKindString(SymbolKind K) { switch (K) { case SymbolKind::Unknown: return ""; case SymbolKind::Module: return "module"; + case SymbolKind::Namespace: return "namespace"; + case SymbolKind::NamespaceAlias: return "namespace-alias"; case SymbolKind::Macro: return "macro"; case SymbolKind::Enum: return "enum"; case SymbolKind::Struct: return "struct"; + case SymbolKind::Class: return "class"; + case SymbolKind::Protocol: return "protocol"; + case SymbolKind::Extension: return "extension"; case SymbolKind::Union: return "union"; - case SymbolKind::Typedef: return "typedef"; + case SymbolKind::TypeAlias: return "type-alias"; case SymbolKind::Function: return "function"; case SymbolKind::Variable: return "variable"; case SymbolKind::Field: return "field"; case SymbolKind::EnumConstant: return "enumerator"; - case SymbolKind::ObjCClass: return "objc-class"; - case SymbolKind::ObjCProtocol: return "objc-protocol"; - case SymbolKind::ObjCCategory: return "objc-category"; - case SymbolKind::ObjCInstanceMethod: return "objc-instance-method"; - case SymbolKind::ObjCClassMethod: return "objc-class-method"; - case SymbolKind::ObjCProperty: return "objc-property"; - case SymbolKind::ObjCIvar: return "objc-ivar"; - case SymbolKind::CXXClass: return "c++-class"; - case SymbolKind::CXXNamespace: return "namespace"; - case SymbolKind::CXXNamespaceAlias: return "namespace-alias"; - case SymbolKind::CXXStaticVariable: return "c++-static-var"; - case SymbolKind::CXXStaticMethod: return "c++-static-method"; - case SymbolKind::CXXInstanceMethod: return "c++-instance-method"; - case SymbolKind::CXXConstructor: return "constructor"; - case SymbolKind::CXXDestructor: return "destructor"; - case SymbolKind::CXXConversionFunction: return "coversion-func"; - case SymbolKind::CXXTypeAlias: return "type-alias"; - case SymbolKind::CXXInterface: return "c++-__interface"; + case SymbolKind::InstanceMethod: return "instance-method"; + case SymbolKind::ClassMethod: return "class-method"; + case SymbolKind::StaticMethod: return "static-method"; + case SymbolKind::InstanceProperty: return "instance-property"; + case SymbolKind::ClassProperty: return "class-property"; + case SymbolKind::StaticProperty: return "static-property"; + case SymbolKind::Constructor: return "constructor"; + case SymbolKind::Destructor: return "destructor"; + case SymbolKind::ConversionFunction: return "coversion-func"; } llvm_unreachable("invalid symbol kind"); } diff --git a/test/Index/Core/index-source.cpp b/test/Index/Core/index-source.cpp index 406f70f3896..75c6396da55 100644 --- a/test/Index/Core/index-source.cpp +++ b/test/Index/Core/index-source.cpp @@ -2,7 +2,7 @@ template class TemplCls { -// CHECK: [[@LINE-1]]:7 | c++-class/C++ | TemplCls | c:@ST>1#T@TemplCls | | Def | rel: 0 +// CHECK: [[@LINE-1]]:7 | class/C++ | TemplCls | c:@ST>1#T@TemplCls | | Def | rel: 0 TemplCls(int x); // CHECK: [[@LINE-1]]:3 | constructor/C++ | TemplCls | c:@ST>1#T@TemplCls@F@TemplCls#I# | | Decl,RelChild | rel: 1 // CHECK-NEXT: RelChild | TemplCls | c:@ST>1#T@TemplCls diff --git a/test/Index/Core/index-source.m b/test/Index/Core/index-source.m index ac309c8e696..0869c0ac7e1 100644 --- a/test/Index/Core/index-source.m +++ b/test/Index/Core/index-source.m @@ -1,9 +1,9 @@ // RUN: c-index-test core -print-source-symbols -- %s -target x86_64-apple-macosx10.7 | FileCheck %s @interface Base -// CHECK: [[@LINE-1]]:12 | objc-class/ObjC | Base | c:objc(cs)Base | _OBJC_CLASS_$_Base | Decl | rel: 0 +// CHECK: [[@LINE-1]]:12 | class/ObjC | Base | c:objc(cs)Base | _OBJC_CLASS_$_Base | Decl | rel: 0 -(void)meth; -// CHECK: [[@LINE-1]]:1 | objc-instance-method/ObjC | meth | c:objc(cs)Base(im)meth | -[Base meth] | Decl,Dyn,RelChild | rel: 1 +// CHECK: [[@LINE-1]]:1 | instance-method/ObjC | meth | c:objc(cs)Base(im)meth | -[Base meth] | Decl,Dyn,RelChild | rel: 1 // CHECK-NEXT: RelChild | Base | c:objc(cs)Base @end @@ -13,28 +13,28 @@ void goo(Base *b) { // CHECK: [[@LINE+2]]:3 | function/C | foo | c:@F@foo | _foo | Ref,Call,RelCall | rel: 1 // CHECK-NEXT: RelCall | goo | c:@F@goo foo(); - // CHECK: [[@LINE+3]]:6 | objc-instance-method/ObjC | meth | c:objc(cs)Base(im)meth | -[Base meth] | Ref,Call,Dyn,RelRec,RelCall | rel: 2 + // CHECK: [[@LINE+3]]:6 | instance-method/ObjC | meth | c:objc(cs)Base(im)meth | -[Base meth] | Ref,Call,Dyn,RelRec,RelCall | rel: 2 // CHECK-NEXT: RelCall | goo | c:@F@goo // CHECK-NEXT: RelRec | Base | c:objc(cs)Base [b meth]; } -// CHECK: [[@LINE+1]]:11 | objc-protocol/ObjC | Prot1 | c:objc(pl)Prot1 | | Decl | rel: 0 +// CHECK: [[@LINE+1]]:11 | protocol/ObjC | Prot1 | c:objc(pl)Prot1 | | Decl | rel: 0 @protocol Prot1 @end -// CHECK: [[@LINE+3]]:11 | objc-protocol/ObjC | Prot2 | c:objc(pl)Prot2 | | Decl | rel: 0 -// CHECK: [[@LINE+2]]:17 | objc-protocol/ObjC | Prot1 | c:objc(pl)Prot1 | | Ref,RelBase | rel: 1 +// CHECK: [[@LINE+3]]:11 | protocol/ObjC | Prot2 | c:objc(pl)Prot2 | | Decl | rel: 0 +// CHECK: [[@LINE+2]]:17 | protocol/ObjC | Prot1 | c:objc(pl)Prot1 | | Ref,RelBase | rel: 1 // CHECK-NEXT: RelBase | Prot2 | c:objc(pl)Prot2 @protocol Prot2 @end -// CHECK: [[@LINE+7]]:12 | objc-class/ObjC | Sub | c:objc(cs)Sub | _OBJC_CLASS_$_Sub | Decl | rel: 0 -// CHECK: [[@LINE+6]]:18 | objc-class/ObjC | Base | c:objc(cs)Base | _OBJC_CLASS_$_Base | Ref,RelBase | rel: 1 +// CHECK: [[@LINE+7]]:12 | class/ObjC | Sub | c:objc(cs)Sub | _OBJC_CLASS_$_Sub | Decl | rel: 0 +// CHECK: [[@LINE+6]]:18 | class/ObjC | Base | c:objc(cs)Base | _OBJC_CLASS_$_Base | Ref,RelBase | rel: 1 // CHECK-NEXT: RelBase | Sub | c:objc(cs)Sub -// CHECK: [[@LINE+4]]:23 | objc-protocol/ObjC | Prot2 | c:objc(pl)Prot2 | | Ref,RelBase | rel: 1 +// CHECK: [[@LINE+4]]:23 | protocol/ObjC | Prot2 | c:objc(pl)Prot2 | | Ref,RelBase | rel: 1 // CHECK-NEXT: RelBase | Sub | c:objc(cs)Sub -// CHECK: [[@LINE+2]]:30 | objc-protocol/ObjC | Prot1 | c:objc(pl)Prot1 | | Ref,RelBase | rel: 1 +// CHECK: [[@LINE+2]]:30 | protocol/ObjC | Prot1 | c:objc(pl)Prot1 | | Ref,RelBase | rel: 1 // CHECK-NEXT: RelBase | Sub | c:objc(cs)Sub @interface Sub : Base @end @@ -66,8 +66,8 @@ -(ObjectType)getit; Two, }; -// CHECK: [[@LINE+1]]:13 | typedef/C | jmp_buf | c:index-source.m@T@jmp_buf | | Def | rel: 0 +// CHECK: [[@LINE+1]]:13 | type-alias/C | jmp_buf | c:index-source.m@T@jmp_buf | | Def | rel: 0 typedef int jmp_buf[(18)]; // CHECK: [[@LINE+2]]:12 | function/C | setjmp | c:@F@setjmp | _setjmp | Decl | rel: 0 -// CHECK: [[@LINE+1]]:19 | typedef/C | jmp_buf | c:index-source.m@T@jmp_buf | | Ref | rel: 0 +// CHECK: [[@LINE+1]]:19 | type-alias/C | jmp_buf | c:index-source.m@T@jmp_buf | | Ref | rel: 0 extern int setjmp(jmp_buf); diff --git a/tools/libclang/CXIndexDataConsumer.cpp b/tools/libclang/CXIndexDataConsumer.cpp index 4f89e43fd7b..bc19d53aeac 100644 --- a/tools/libclang/CXIndexDataConsumer.cpp +++ b/tools/libclang/CXIndexDataConsumer.cpp @@ -1126,7 +1126,7 @@ void CXIndexDataConsumer::translateLoc(SourceLocation Loc, *offset = FileOffset; } -static CXIdxEntityKind getEntityKindFromSymbolKind(SymbolKind K); +static CXIdxEntityKind getEntityKindFromSymbolKind(SymbolKind K, SymbolLanguage L); static CXIdxEntityCXXTemplateKind getEntityKindFromSymbolCXXTemplateKind(SymbolCXXTemplateKind K); static CXIdxEntityLanguage getEntityLangFromSymbolLang(SymbolLanguage L); @@ -1143,7 +1143,7 @@ void CXIndexDataConsumer::getEntityInfo(const NamedDecl *D, EntityInfo.IndexCtx = this; SymbolInfo SymInfo = getSymbolInfo(D); - EntityInfo.kind = getEntityKindFromSymbolKind(SymInfo.Kind); + EntityInfo.kind = getEntityKindFromSymbolKind(SymInfo.Kind, SymInfo.Lang); EntityInfo.templateKind = getEntityKindFromSymbolCXXTemplateKind(SymInfo.TemplateKind); EntityInfo.lang = getEntityLangFromSymbolLang(SymInfo.Lang); @@ -1236,40 +1236,50 @@ bool CXIndexDataConsumer::isTemplateImplicitInstantiation(const Decl *D) { return false; } -static CXIdxEntityKind getEntityKindFromSymbolKind(SymbolKind K) { +static CXIdxEntityKind getEntityKindFromSymbolKind(SymbolKind K, SymbolLanguage Lang) { switch (K) { case SymbolKind::Unknown: case SymbolKind::Module: case SymbolKind::Macro: + case SymbolKind::ClassProperty: return CXIdxEntity_Unexposed; case SymbolKind::Enum: return CXIdxEntity_Enum; case SymbolKind::Struct: return CXIdxEntity_Struct; case SymbolKind::Union: return CXIdxEntity_Union; - case SymbolKind::Typedef: return CXIdxEntity_Typedef; + case SymbolKind::TypeAlias: + if (Lang == SymbolLanguage::CXX) + return CXIdxEntity_CXXTypeAlias; + return CXIdxEntity_Typedef; case SymbolKind::Function: return CXIdxEntity_Function; case SymbolKind::Variable: return CXIdxEntity_Variable; - case SymbolKind::Field: return CXIdxEntity_Field; + case SymbolKind::Field: + if (Lang == SymbolLanguage::ObjC) + return CXIdxEntity_ObjCIvar; + return CXIdxEntity_Field; case SymbolKind::EnumConstant: return CXIdxEntity_EnumConstant; - case SymbolKind::ObjCClass: return CXIdxEntity_ObjCClass; - case SymbolKind::ObjCProtocol: return CXIdxEntity_ObjCProtocol; - case SymbolKind::ObjCCategory: return CXIdxEntity_ObjCCategory; - case SymbolKind::ObjCInstanceMethod: return CXIdxEntity_ObjCInstanceMethod; - case SymbolKind::ObjCClassMethod: return CXIdxEntity_ObjCClassMethod; - case SymbolKind::ObjCProperty: return CXIdxEntity_ObjCProperty; - case SymbolKind::ObjCIvar: return CXIdxEntity_ObjCIvar; - case SymbolKind::CXXClass: return CXIdxEntity_CXXClass; - case SymbolKind::CXXNamespace: return CXIdxEntity_CXXNamespace; - case SymbolKind::CXXNamespaceAlias: return CXIdxEntity_CXXNamespaceAlias; - case SymbolKind::CXXStaticVariable: return CXIdxEntity_CXXStaticVariable; - case SymbolKind::CXXStaticMethod: return CXIdxEntity_CXXStaticMethod; - case SymbolKind::CXXInstanceMethod: return CXIdxEntity_CXXInstanceMethod; - case SymbolKind::CXXConstructor: return CXIdxEntity_CXXConstructor; - case SymbolKind::CXXDestructor: return CXIdxEntity_CXXDestructor; - case SymbolKind::CXXConversionFunction: - return CXIdxEntity_CXXConversionFunction; - case SymbolKind::CXXTypeAlias: return CXIdxEntity_CXXTypeAlias; - case SymbolKind::CXXInterface: return CXIdxEntity_CXXInterface; + case SymbolKind::Class: + if (Lang == SymbolLanguage::ObjC) + return CXIdxEntity_ObjCClass; + return CXIdxEntity_CXXClass; + case SymbolKind::Protocol: + if (Lang == SymbolLanguage::ObjC) + return CXIdxEntity_ObjCProtocol; + return CXIdxEntity_CXXInterface; + case SymbolKind::Extension: return CXIdxEntity_ObjCCategory; + case SymbolKind::InstanceMethod: + if (Lang == SymbolLanguage::ObjC) + return CXIdxEntity_ObjCInstanceMethod; + return CXIdxEntity_CXXInstanceMethod; + case SymbolKind::ClassMethod: return CXIdxEntity_ObjCClassMethod; + case SymbolKind::StaticMethod: return CXIdxEntity_CXXStaticMethod; + case SymbolKind::InstanceProperty: return CXIdxEntity_ObjCProperty; + case SymbolKind::StaticProperty: return CXIdxEntity_CXXStaticVariable; + case SymbolKind::Namespace: return CXIdxEntity_CXXNamespace; + case SymbolKind::NamespaceAlias: return CXIdxEntity_CXXNamespaceAlias; + case SymbolKind::Constructor: return CXIdxEntity_CXXConstructor; + case SymbolKind::Destructor: return CXIdxEntity_CXXDestructor; + case SymbolKind::ConversionFunction: return CXIdxEntity_CXXConversionFunction; } llvm_unreachable("invalid symbol kind"); } From 21aacc97526a1a53d1450710505215c5289a4c7d Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Thu, 31 Mar 2016 20:18:22 +0000 Subject: [PATCH 391/742] [index] Fix regression where ObjC method declarations may mistakenly get indexed as definition. rdar://25372906 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265042 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Index/IndexingContext.cpp | 6 ++--- test/Index/Core/index-source.m | 34 ++++++++++++++++++++++++++ test/Index/index-decls.m | 9 +++++++ tools/libclang/CXIndexDataConsumer.cpp | 28 ++++++++++++--------- 4 files changed, 63 insertions(+), 14 deletions(-) diff --git a/lib/Index/IndexingContext.cpp b/lib/Index/IndexingContext.cpp index 3d1d9902dd6..424c1034ae4 100644 --- a/lib/Index/IndexingContext.cpp +++ b/lib/Index/IndexingContext.cpp @@ -171,7 +171,7 @@ static const Decl *adjustTemplateImplicitInstantiation(const Decl *D) { return nullptr; } -static bool isDeclADefinition(const Decl *D, ASTContext &Ctx) { +static bool isDeclADefinition(const Decl *D, const DeclContext *ContainerDC, ASTContext &Ctx) { if (auto VD = dyn_cast(D)) return VD->isThisDeclarationADefinition(Ctx); @@ -182,7 +182,7 @@ static bool isDeclADefinition(const Decl *D, ASTContext &Ctx) { return TD->isThisDeclarationADefinition(); if (auto MD = dyn_cast(D)) - return MD->isThisDeclarationADefinition(); + return MD->isThisDeclarationADefinition() || isa(ContainerDC); if (isa(D) || isa(D) || @@ -284,7 +284,7 @@ bool IndexingContext::handleDeclOccurrence(const Decl *D, SourceLocation Loc, if (IsRef) Roles |= (unsigned)SymbolRole::Reference; - else if (isDeclADefinition(D, *Ctx)) + else if (isDeclADefinition(D, ContainerDC, *Ctx)) Roles |= (unsigned)SymbolRole::Definition; else Roles |= (unsigned)SymbolRole::Declaration; diff --git a/test/Index/Core/index-source.m b/test/Index/Core/index-source.m index 0869c0ac7e1..2e9c01c02d4 100644 --- a/test/Index/Core/index-source.m +++ b/test/Index/Core/index-source.m @@ -71,3 +71,37 @@ -(ObjectType)getit; // CHECK: [[@LINE+2]]:12 | function/C | setjmp | c:@F@setjmp | _setjmp | Decl | rel: 0 // CHECK: [[@LINE+1]]:19 | type-alias/C | jmp_buf | c:index-source.m@T@jmp_buf | | Ref | rel: 0 extern int setjmp(jmp_buf); + +@class I1; +@interface I1 +// CHECK: [[@LINE+1]]:1 | instance-method/ObjC | meth | c:objc(cs)I1(im)meth | -[I1 meth] | Decl,Dyn,RelChild | rel: 1 +-(void)meth; +@end + +@interface I2 +@property (readwrite) id prop; +@end + +// CHECK: [[@LINE+2]]:17 | field/ObjC | _prop | c:objc(cs)I2@_prop | | Def,Impl,RelChild | rel: 1 +// CHECK-NEXT: RelChild | I2 | c:objc(cs)I2 +@implementation I2 +// CHECK: [[@LINE+5]]:13 | instance-property/ObjC | prop | c:objc(cs)I2(py)prop | | Ref | rel: 0 +// CHECK: [[@LINE+4]]:13 | instance-method/ObjC | prop | c:objc(cs)I2(im)prop | -[I2 prop] | Def,RelChild | rel: 1 +// CHECK-NEXT: RelChild | I2 | c:objc(cs)I2 +// CHECK: [[@LINE+2]]:13 | instance-method/ObjC | setProp: | c:objc(cs)I2(im)setProp: | -[I2 setProp:] | Def,RelChild | rel: 1 +// CHECK-NEXT: RelChild | I2 | c:objc(cs)I2 +@synthesize prop = _prop; +@end + +@interface I3 +@property (readwrite) id prop; +-(id)prop; +-(void)setProp:(id)p; +@end + +@implementation I3 +// CHECK: [[@LINE+3]]:13 | instance-property/ObjC | prop | c:objc(cs)I3(py)prop | | Ref | rel: 0 +// CHECK: [[@LINE+2]]:13 | instance-method/ObjC | prop | c:objc(cs)I3(im)prop | -[I3 prop] | Def,RelChild | rel: 1 +// CHECK: [[@LINE+1]]:13 | instance-method/ObjC | setProp: | c:objc(cs)I3(im)setProp: | -[I3 setProp:] | Def,RelChild | rel: 1 +@synthesize prop = _prop; +@end diff --git a/test/Index/index-decls.m b/test/Index/index-decls.m index a405abc78c8..3564117523e 100644 --- a/test/Index/index-decls.m +++ b/test/Index/index-decls.m @@ -48,6 +48,12 @@ -(void)setProp:(id)p { } @end +// rdar://25372906 +@class I5; +@interface I5 +-(void)meth; +@end + // RUN: c-index-test -index-file %s -target x86_64-apple-macosx10.7 > %t // RUN: FileCheck %s -input-file=%t // CHECK: [indexDeclaration]: kind: objc-class | name: I | {{.*}} | loc: 1:12 @@ -71,5 +77,8 @@ -(void)setProp:(id)p { // CHECK: [indexEntityReference]: kind: function | name: extfn | {{.*}} | loc: 33:10 // CHECK: [indexDeclaration]: kind: objc-class | name: I4 | {{.*}} | loc: 36:12 +// CHECK: [indexEntityReference]: kind: objc-property | name: prop | {{.*}} | cursor: ObjCSynthesizeDecl=prop:37:34 (Definition) | loc: 43:13 | :: kind: objc-class | name: I4 | {{.*}} | container: [I4:42:17] | refkind: direct // CHECK-NOT: [indexDeclaration]: kind: objc-instance-method {{.*}} loc: 37: // CHECK-NOT: [indexDeclaration]: kind: objc-instance-method {{.*}} loc: 43: + +// CHECK: [indexDeclaration]: kind: objc-instance-method | name: meth | {{.*}} loc: 54:1 | {{.*}} | isRedecl: 0 | isDef: 0 | diff --git a/tools/libclang/CXIndexDataConsumer.cpp b/tools/libclang/CXIndexDataConsumer.cpp index bc19d53aeac..3b556d441c7 100644 --- a/tools/libclang/CXIndexDataConsumer.cpp +++ b/tools/libclang/CXIndexDataConsumer.cpp @@ -92,7 +92,7 @@ class IndexingDeclVisitor : public ConstDeclVisitor { } bool VisitObjCMethodDecl(const ObjCMethodDecl *D) { - if (D->getDeclContext() != LexicalDC) + if (isa(LexicalDC) && !D->isThisDeclarationADefinition()) DataConsumer.handleSynthesizedObjCMethod(D, DeclLoc, LexicalDC); else DataConsumer.handleObjCMethod(D); @@ -191,22 +191,28 @@ bool CXIndexDataConsumer::handleDeclOccurence(const Decl *D, cast(ASTNode.ContainerDC), getCXTU()); } else { - const NamedDecl *CursorD = dyn_cast_or_null(ASTNode.OrigD); - if (!CursorD) - CursorD = ND; - Cursor = getRefCursor(CursorD, Loc); + if (ASTNode.OrigD) { + if (auto *OrigND = dyn_cast(ASTNode.OrigD)) + Cursor = getRefCursor(OrigND, Loc); + else + Cursor = MakeCXCursor(ASTNode.OrigD, CXTU); + } else { + Cursor = getRefCursor(ND, Loc); + } } handleReference(ND, Loc, Cursor, dyn_cast_or_null(ASTNode.Parent), ASTNode.ContainerDC, ASTNode.OrigE, Kind); } else { - const DeclContext *DC = nullptr; - for (const auto &SymRel : Relations) { - if (SymRel.Roles & (unsigned)SymbolRole::RelationChildOf) - DC = dyn_cast(SymRel.RelatedSymbol); + const DeclContext *LexicalDC = ASTNode.ContainerDC; + if (!LexicalDC) { + for (const auto &SymRel : Relations) { + if (SymRel.Roles & (unsigned)SymbolRole::RelationChildOf) + LexicalDC = dyn_cast(SymRel.RelatedSymbol); + } } - IndexingDeclVisitor(*this, Loc, DC).Visit(ASTNode.OrigD); + IndexingDeclVisitor(*this, Loc, LexicalDC).Visit(ASTNode.OrigD); } return !shouldAbort(); @@ -816,7 +822,7 @@ bool CXIndexDataConsumer::handleSynthesizedObjCMethod(const ObjCMethodDecl *D, const DeclContext *LexicalDC) { DeclInfo DInfo(/*isRedeclaration=*/true, /*isDefinition=*/true, /*isContainer=*/false); - return handleDecl(D, Loc, getCursor(D), DInfo, LexicalDC, LexicalDC); + return handleDecl(D, Loc, getCursor(D), DInfo, LexicalDC, D->getDeclContext()); } bool CXIndexDataConsumer::handleObjCProperty(const ObjCPropertyDecl *D) { From 8067253bd5135d62ed96e922dde3aa2184d0829f Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Fri, 1 Apr 2016 03:24:13 +0000 Subject: [PATCH 392/742] [analyzer] Prefer accessor method in extension over category in CallEvent. In ObjCMethodCall:getRuntimeDefinition(), if the method is an accessor in a category, and it doesn't have a self declaration, first try to find the method in a class extension. This works around a bug in Sema where multiple accessors are synthesized for properties in class extensions that are redeclared in a category. The implicit parameters are not filled in for the method on the category, which causes a crash when trying to synthesize a getter for the property in BodyFarm. The Sema bug is tracked as rdar://problem/25481164. rdar://problem/25056531 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265103 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 9a9ef84ee01c50e33dd065bb549a030b79c61beb) --- lib/StaticAnalyzer/Core/CallEvent.cpp | 26 ++++++++++++-- test/Analysis/properties.m | 49 +++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp index 2c7b5302dd6..626775846bb 100644 --- a/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -958,8 +958,30 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const { // even if we don't actually have an implementation. if (!*Val) if (const ObjCMethodDecl *CompileTimeMD = E->getMethodDecl()) - if (CompileTimeMD->isPropertyAccessor()) - Val = IDecl->lookupInstanceMethod(Sel); + if (CompileTimeMD->isPropertyAccessor()) { + if (!CompileTimeMD->getSelfDecl() && + isa(CompileTimeMD->getDeclContext())) { + // If the method is an accessor in a category, and it doesn't + // have a self declaration, first + // try to find the method in a class extension. This + // works around a bug in Sema where multiple accessors + // are synthesized for properties in class + // extensions that are redeclared in a category and the + // the implicit parameters are not filled in for + // the method on the category. + // This ensures we find the accessor in the extension, which + // has the implicit parameters filled in. + auto *ID = CompileTimeMD->getClassInterface(); + for (auto *CatDecl : ID->visible_extensions()) { + Val = CatDecl->getMethod(Sel, + CompileTimeMD->isInstanceMethod()); + if (*Val) + break; + } + } + if (!*Val) + Val = IDecl->lookupInstanceMethod(Sel); + } } const ObjCMethodDecl *MD = Val.getValue(); diff --git a/test/Analysis/properties.m b/test/Analysis/properties.m index d79643f8b60..3b0312507e9 100644 --- a/test/Analysis/properties.m +++ b/test/Analysis/properties.m @@ -247,6 +247,55 @@ - (void)testSynthesisForShadowedReadWriteProperties; { } @end +// Tests for the analyzer fix that works around a Sema bug +// where multiple methods are created for properties in class extensions that +// are redeclared in a category method. +// The Sema bug is tracked as . +@interface ClassWithRedeclaredPropertyInExtensionFollowedByCategory +@end + +@interface ClassWithRedeclaredPropertyInExtensionFollowedByCategory () +@end + +@interface ClassWithRedeclaredPropertyInExtensionFollowedByCategory () +@property (readwrite) int someProp; +@property (readonly) int otherProp; +@end + +@interface ClassWithRedeclaredPropertyInExtensionFollowedByCategory (MyCat) +@property (readonly) int someProp; +@property (readonly) int otherProp; +@end + +@implementation ClassWithRedeclaredPropertyInExtensionFollowedByCategory +- (void)testSynthesisForRedeclaredProperties; { + clang_analyzer_eval(self.someProp == self.someProp); // expected-warning{{TRUE}} + clang_analyzer_eval([self someProp] == self.someProp); // expected-warning{{TRUE}} + + clang_analyzer_eval(self.otherProp == self.otherProp); // expected-warning{{TRUE}} + clang_analyzer_eval([self otherProp] == self.otherProp); // expected-warning{{TRUE}} +} +@end + +// The relative order of the extension and the category matter, so test both. +@interface ClassWithRedeclaredPropertyInCategoryFollowedByExtension +@end + +@interface ClassWithRedeclaredPropertyInCategoryFollowedByExtension () +@property (readwrite) int someProp; +@end + +@interface ClassWithRedeclaredPropertyInCategoryFollowedByExtension (MyCat) +@property (readonly) int someProp; +@end + +@implementation ClassWithRedeclaredPropertyInCategoryFollowedByExtension +- (void)testSynthesisForRedeclaredProperties; { + clang_analyzer_eval(self.someProp == self.someProp); // expected-warning{{TRUE}} + clang_analyzer_eval([self someProp] == self.someProp); // expected-warning{{TRUE}} +} +@end + @interface ClassWithSynthesizedPropertyAndGetter @property (readonly) int someProp; @end From 8980804943df4f4281c69a9416423fe2c41c204c Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Thu, 31 Mar 2016 06:36:07 +0000 Subject: [PATCH 393/742] [CodeGenCXX] Fix ItaniumCXXABI::getAlignmentOfExnObject to return 8-byte alignment on Darwin. Itanium C++ ABI specifies that _Unwind_Exception should be double-word aligned (16B). To conform to the ABI, libraries implementing exception handling declare the struct with __attribute__((aligned)), which aligns the unwindHeader field (and the end of __cxa_exception) to the default target alignment (which is typically 16-bytes). struct __cxa_exception { ... // struct is declared with __attribute__((aligned)). _Unwind_Exception unwindHeader; }; Based on the assumption that _Unwind_Exception is declared with __attribute__((aligned)), ItaniumCXXABI::getAlignmentOfExnObject returns the target default alignment for __attribute__((aligned)). It turns out that libc++abi, which is used on Darwin, doesn't declare the struct with the attribute and therefore doesn't guarantee that unwindHeader is aligned to the alignment specified by the ABI, which in some cases causes the program to crash because of unaligned memory accesses. This commit avoids crashes due to unaligned memory accesses by having getAlignmentOfExnObject return an 8-byte alignment on Darwin. I've only fixed the problem for Darwin, but we should also figure out whether other platforms using libc++abi need similar fixes. rdar://problem/25314277 Differential revision: http://reviews.llvm.org/D18479 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@264998 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit d3491bd078c477e68d6a3aec1df37c63e255371c) --- include/clang/Basic/TargetInfo.h | 13 +++++++++++++ lib/Basic/Targets.cpp | 7 +++++++ lib/CodeGen/ItaniumCXXABI.cpp | 12 ++---------- test/CodeGenCXX/eh.cpp | 22 ++++++++++++++++++++++ 4 files changed, 44 insertions(+), 10 deletions(-) diff --git a/include/clang/Basic/TargetInfo.h b/include/clang/Basic/TargetInfo.h index f1d83381707..2d12cb6f62a 100644 --- a/include/clang/Basic/TargetInfo.h +++ b/include/clang/Basic/TargetInfo.h @@ -407,6 +407,19 @@ class TargetInfo : public RefCountedBase { /// types for the given target. unsigned getSimdDefaultAlign() const { return SimdDefaultAlign; } + /// Return the alignment (in bits) of the thrown exception object. + virtual unsigned getExnObjectAlignment() const { + /// Itanium says that an _Unwind_Exception has to be "double-word" + /// aligned (and thus the end of it is also so-aligned), meaning 16 + /// bytes. Of course, that was written for the actual Itanium, + /// which is a 64-bit platform. Classically, the ABI doesn't really + /// specify the alignment on other platforms, but in practice + /// libUnwind declares the struct with __attribute__((aligned)), so + /// we assume that alignment here. (It's generally 16 bytes, but + /// some targets overwrite it.) + return getDefaultAlignForAttributeAligned(); + } + /// \brief Return the size of intmax_t and uintmax_t for this target, in bits. unsigned getIntMaxTWidth() const { return getTypeWidth(IntMaxType); diff --git a/lib/Basic/Targets.cpp b/lib/Basic/Targets.cpp index 3eacaf0d5ce..8920589eb56 100644 --- a/lib/Basic/Targets.cpp +++ b/lib/Basic/Targets.cpp @@ -264,6 +264,13 @@ class DarwinTargetInfo : public OSTargetInfo { bool hasProtectedVisibility() const override { return false; } + + unsigned getExnObjectAlignment() const override { + // The alignment of an exception object is 8-bytes for darwin since + // libc++abi doesn't declare _Unwind_Exception with __attribute__((aligned)) + // and therefore doesn't guarantee 16-byte alignment. + return 64; + } }; diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp index b339e3eac11..84f43dd3a8e 100644 --- a/lib/CodeGen/ItaniumCXXABI.cpp +++ b/lib/CodeGen/ItaniumCXXABI.cpp @@ -154,17 +154,9 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI { Address Ptr, QualType ElementType, const CXXDestructorDecl *Dtor) override; - /// Itanium says that an _Unwind_Exception has to be "double-word" - /// aligned (and thus the end of it is also so-aligned), meaning 16 - /// bytes. Of course, that was written for the actual Itanium, - /// which is a 64-bit platform. Classically, the ABI doesn't really - /// specify the alignment on other platforms, but in practice - /// libUnwind declares the struct with __attribute__((aligned)), so - /// we assume that alignment here. (It's generally 16 bytes, but - /// some targets overwrite it.) CharUnits getAlignmentOfExnObject() { - auto align = CGM.getContext().getTargetDefaultAlignForAttributeAligned(); - return CGM.getContext().toCharUnitsFromBits(align); + unsigned Align = CGM.getContext().getTargetInfo().getExnObjectAlignment(); + return CGM.getContext().toCharUnitsFromBits(Align); } void emitRethrow(CodeGenFunction &CGF, bool isNoReturn) override; diff --git a/test/CodeGenCXX/eh.cpp b/test/CodeGenCXX/eh.cpp index b44e8144bb2..db0576a1bae 100644 --- a/test/CodeGenCXX/eh.cpp +++ b/test/CodeGenCXX/eh.cpp @@ -448,5 +448,27 @@ namespace test16 { } } +namespace test17 { +class BaseException { +private: + int a[4]; +public: + BaseException() {}; +}; + +class DerivedException: public BaseException { +}; + +int foo() { + throw DerivedException(); + // The alignment passed to memset is 8, not 16, on Darwin. + + // CHECK: [[T0:%.*]] = call i8* @__cxa_allocate_exception(i64 16) + // CHECK-NEXT: [[T1:%.*]] = bitcast i8* [[T0]] to %"class.test17::DerivedException"* + // CHECK-NEXT: [[T2:%.*]] = bitcast %"class.test17::DerivedException"* [[T1]] to i8* + // CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* [[T2]], i8 0, i64 16, i32 8, i1 false) +} +} + // CHECK: attributes [[NUW]] = { nounwind } // CHECK: attributes [[NR]] = { noreturn } From 909e43f1995966e673450923bd1eadb9042be682 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Thu, 31 Mar 2016 18:32:38 +0000 Subject: [PATCH 394/742] Fix the comment for the function added in r264998. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265035 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 142c5b38d4100a34e67da6190a994caf780dd010) --- include/clang/Basic/TargetInfo.h | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/include/clang/Basic/TargetInfo.h b/include/clang/Basic/TargetInfo.h index 2d12cb6f62a..7f1bf349218 100644 --- a/include/clang/Basic/TargetInfo.h +++ b/include/clang/Basic/TargetInfo.h @@ -407,16 +407,18 @@ class TargetInfo : public RefCountedBase { /// types for the given target. unsigned getSimdDefaultAlign() const { return SimdDefaultAlign; } - /// Return the alignment (in bits) of the thrown exception object. + /// Return the alignment (in bits) of the thrown exception object. This is + /// only meaningful for targets that allocate C++ exceptions in a system + /// runtime, such as those using the Itanium C++ ABI. virtual unsigned getExnObjectAlignment() const { - /// Itanium says that an _Unwind_Exception has to be "double-word" - /// aligned (and thus the end of it is also so-aligned), meaning 16 - /// bytes. Of course, that was written for the actual Itanium, - /// which is a 64-bit platform. Classically, the ABI doesn't really - /// specify the alignment on other platforms, but in practice - /// libUnwind declares the struct with __attribute__((aligned)), so - /// we assume that alignment here. (It's generally 16 bytes, but - /// some targets overwrite it.) + // Itanium says that an _Unwind_Exception has to be "double-word" + // aligned (and thus the end of it is also so-aligned), meaning 16 + // bytes. Of course, that was written for the actual Itanium, + // which is a 64-bit platform. Classically, the ABI doesn't really + // specify the alignment on other platforms, but in practice + // libUnwind declares the struct with __attribute__((aligned)), so + // we assume that alignment here. (It's generally 16 bytes, but + // some targets overwrite it.) return getDefaultAlignForAttributeAligned(); } From c80a4d6d1779e71832fdc7b86a57dc54a3953300 Mon Sep 17 00:00:00 2001 From: Roman Levenstein Date: Wed, 16 Mar 2016 18:00:46 +0000 Subject: [PATCH 395/742] Add attributes for preserve_mostcc/preserve_allcc calling conventions to the C/C++ front-end Till now, preserve_mostcc/preserve_allcc calling convention attributes were only available at the LLVM IR level. This patch adds attributes for preserve_mostcc/preserve_allcc calling conventions to the C/C++ front-end. The code was mostly written by Juergen Ributzka. I just added support for the AArch64 target and tests. Differential Revision: http://reviews.llvm.org/D18025 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263647 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang-c/Index.h | 2 + include/clang/AST/Type.h | 2 + include/clang/Basic/Attr.td | 10 +++++ include/clang/Basic/AttrDocs.td | 67 +++++++++++++++++++++++++++++++ include/clang/Basic/Specifiers.h | 4 +- lib/AST/Type.cpp | 6 +++ lib/AST/TypePrinter.cpp | 12 ++++++ lib/Basic/Targets.cpp | 26 ++++++++++-- lib/CodeGen/CGCall.cpp | 8 ++++ lib/Sema/SemaDeclAttr.cpp | 13 +++++- lib/Sema/SemaType.cpp | 12 +++++- test/CodeGen/preserve-call-conv.c | 17 ++++++++ test/Sema/preserve-call-conv.c | 35 ++++++++++++++++ tools/libclang/CXType.cpp | 2 + 14 files changed, 209 insertions(+), 7 deletions(-) create mode 100644 test/CodeGen/preserve-call-conv.c create mode 100644 test/Sema/preserve-call-conv.c diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h index 4ecbed5f19a..cbb5052ecfc 100644 --- a/include/clang-c/Index.h +++ b/include/clang-c/Index.h @@ -2943,6 +2943,8 @@ enum CXCallingConv { CXCallingConv_X86_64Win64 = 10, CXCallingConv_X86_64SysV = 11, CXCallingConv_X86VectorCall = 12, + CXCallingConv_PreserveMost = 14, + CXCallingConv_PreserveAll = 15, CXCallingConv_Invalid = 100, CXCallingConv_Unexposed = 200 diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index d63b2c43d55..2929037a87c 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -3630,6 +3630,8 @@ class AttributedType : public Type, public llvm::FoldingSetNode { attr_inteloclbicc, attr_ms_abi, attr_sysv_abi, + attr_preserve_most, + attr_preserve_all, attr_ptr32, attr_ptr64, attr_sptr, diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 9daf3a766e3..cec4b9b2780 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -1422,6 +1422,16 @@ def Pascal : InheritableAttr { let Documentation = [Undocumented]; } +def PreserveMost : InheritableAttr { + let Spellings = [GNU<"preserve_most">]; + let Documentation = [PreserveMostDocs]; +} + +def PreserveAll : InheritableAttr { + let Spellings = [GNU<"preserve_all">]; + let Documentation = [PreserveAllDocs]; +} + def Target : InheritableAttr { let Spellings = [GCC<"target">]; let Args = [StringArgument<"featuresStr">]; diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td index d27cdd9f92d..d6c901ae412 100644 --- a/include/clang/Basic/AttrDocs.td +++ b/include/clang/Basic/AttrDocs.td @@ -1954,3 +1954,70 @@ hardware design, touch the red zone. The system will crash if the wrong handler is used. }]; } + +def PreserveMostDocs : Documentation { + let Category = DocCatCallingConvs; + let Content = [{ +On X86-64 and AArch64 targets, this attribute changes the calling convention of +a function. The ``preserve_most`` calling convention attempts to make the code +in the caller as unintrusive as possible. This convention behaves identically +to the ``C`` calling convention on how arguments and return values are passed, +but it uses a different set of caller/callee-saved registers. This alleviates +the burden of saving and recovering a large register set before and after the +call in the caller. If the arguments are passed in callee-saved registers, +then they will be preserved by the callee across the call. This doesn't +apply for values returned in callee-saved registers. + +- On X86-64 the callee preserves all general purpose registers, except for + R11. R11 can be used as a scratch register. Floating-point registers + (XMMs/YMMs) are not preserved and need to be saved by the caller. + +The idea behind this convention is to support calls to runtime functions +that have a hot path and a cold path. The hot path is usually a small piece +of code that doesn't use many registers. The cold path might need to call out to +another function and therefore only needs to preserve the caller-saved +registers, which haven't already been saved by the caller. The +`preserve_most` calling convention is very similar to the ``cold`` calling +convention in terms of caller/callee-saved registers, but they are used for +different types of function calls. ``coldcc`` is for function calls that are +rarely executed, whereas `preserve_most` function calls are intended to be +on the hot path and definitely executed a lot. Furthermore ``preserve_most`` +doesn't prevent the inliner from inlining the function call. + +This calling convention will be used by a future version of the Objective-C +runtime and should therefore still be considered experimental at this time. +Although this convention was created to optimize certain runtime calls to +the Objective-C runtime, it is not limited to this runtime and might be used +by other runtimes in the future too. The current implementation only +supports X86-64 and AArch64, but the intention is to support more architectures +in the future. + }]; +} + +def PreserveAllDocs : Documentation { + let Category = DocCatCallingConvs; + let Content = [{ +On X86-64 and AArch64 targets, this attribute changes the calling convention of +a function. The ``preserve_all`` calling convention attempts to make the code +in the caller even less intrusive than the ``preserve_most`` calling convention. +This calling convention also behaves identical to the ``C`` calling convention +on how arguments and return values are passed, but it uses a different set of +caller/callee-saved registers. This removes the burden of saving and +recovering a large register set before and after the call in the caller. If +the arguments are passed in callee-saved registers, then they will be +preserved by the callee across the call. This doesn't apply for values +returned in callee-saved registers. + +- On X86-64 the callee preserves all general purpose registers, except for + R11. R11 can be used as a scratch register. Furthermore it also preserves + all floating-point registers (XMMs/YMMs). + +The idea behind this convention is to support calls to runtime functions +that don't need to call out to any other functions. + +This calling convention, like the ``preserve_most`` calling convention, will be +used by a future version of the Objective-C runtime and should be considered +experimental at this time. + }]; +} + diff --git a/include/clang/Basic/Specifiers.h b/include/clang/Basic/Specifiers.h index e284171f777..c9529eb223f 100644 --- a/include/clang/Basic/Specifiers.h +++ b/include/clang/Basic/Specifiers.h @@ -238,7 +238,9 @@ namespace clang { CC_AAPCS_VFP, // __attribute__((pcs("aapcs-vfp"))) CC_IntelOclBicc, // __attribute__((intel_ocl_bicc)) CC_SpirFunction, // default for OpenCL functions on SPIR target - CC_SpirKernel // inferred for OpenCL kernels on SPIR target + CC_SpirKernel, // inferred for OpenCL kernels on SPIR target + CC_PreserveMost, // __attribute__((preserve_most)) + CC_PreserveAll, // __attribute__((preserve_all)) }; /// \brief Checks whether the given calling convention supports variadic diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index b467dac66b5..721b8b21a73 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -2655,6 +2655,8 @@ StringRef FunctionType::getNameForCallConv(CallingConv CC) { case CC_IntelOclBicc: return "intel_ocl_bicc"; case CC_SpirFunction: return "spir_function"; case CC_SpirKernel: return "spir_kernel"; + case CC_PreserveMost: return "preserve_most"; + case CC_PreserveAll: return "preserve_all"; } llvm_unreachable("Invalid calling convention."); @@ -2996,6 +2998,8 @@ bool AttributedType::isQualifier() const { case AttributedType::attr_pascal: case AttributedType::attr_vectorcall: case AttributedType::attr_inteloclbicc: + case AttributedType::attr_preserve_most: + case AttributedType::attr_preserve_all: case AttributedType::attr_ms_abi: case AttributedType::attr_sysv_abi: case AttributedType::attr_ptr32: @@ -3052,6 +3056,8 @@ bool AttributedType::isCallingConv() const { case attr_ms_abi: case attr_sysv_abi: case attr_inteloclbicc: + case attr_preserve_most: + case attr_preserve_all: return true; } llvm_unreachable("invalid attr kind"); diff --git a/lib/AST/TypePrinter.cpp b/lib/AST/TypePrinter.cpp index b202523bdaf..762033b600e 100644 --- a/lib/AST/TypePrinter.cpp +++ b/lib/AST/TypePrinter.cpp @@ -703,6 +703,12 @@ void TypePrinter::printFunctionProtoAfter(const FunctionProtoType *T, case CC_SpirKernel: // Do nothing. These CCs are not available as attributes. break; + case CC_PreserveMost: + OS << " __attribute__((preserve_most))"; + break; + case CC_PreserveAll: + OS << " __attribute__((preserve_all))"; + break; } } @@ -1321,6 +1327,12 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, break; } case AttributedType::attr_inteloclbicc: OS << "inteloclbicc"; break; + case AttributedType::attr_preserve_most: + OS << "preserve_most"; + break; + case AttributedType::attr_preserve_all: + OS << "preserve_all"; + break; } OS << "))"; } diff --git a/lib/Basic/Targets.cpp b/lib/Basic/Targets.cpp index d75aa3e0095..af2a8c9c590 100644 --- a/lib/Basic/Targets.cpp +++ b/lib/Basic/Targets.cpp @@ -4017,10 +4017,17 @@ class X86_64TargetInfo : public X86TargetInfo { } CallingConvCheckResult checkCallingConvention(CallingConv CC) const override { - return (CC == CC_C || - CC == CC_X86VectorCall || - CC == CC_IntelOclBicc || - CC == CC_X86_64Win64) ? CCCR_OK : CCCR_Warning; + switch (CC) { + case CC_C: + case CC_X86VectorCall: + case CC_IntelOclBicc: + case CC_X86_64Win64: + case CC_PreserveMost: + case CC_PreserveAll: + return CCCR_OK; + default: + return CCCR_Warning; + } } CallingConv getDefaultCallingConv(CallingConvMethodType MT) const override { @@ -5448,6 +5455,17 @@ class AArch64TargetInfo : public TargetInfo { return true; } + CallingConvCheckResult checkCallingConvention(CallingConv CC) const override { + switch (CC) { + case CC_C: + case CC_PreserveMost: + case CC_PreserveAll: + return CCCR_OK; + default: + return CCCR_Warning; + } + } + bool isCLZForZeroUndef() const override { return false; } BuiltinVaListKind getBuiltinVaListKind() const override { diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index 715e8e7aadc..8a9c8cd3fe4 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -56,6 +56,8 @@ static unsigned ClangCallConvToLLVMCallConv(CallingConv CC) { case CC_X86VectorCall: return llvm::CallingConv::X86_VectorCall; case CC_SpirFunction: return llvm::CallingConv::SPIR_FUNC; case CC_SpirKernel: return llvm::CallingConv::SPIR_KERNEL; + case CC_PreserveMost: return llvm::CallingConv::PreserveMost; + case CC_PreserveAll: return llvm::CallingConv::PreserveAll; } } @@ -173,6 +175,12 @@ static CallingConv getCallingConventionForDecl(const Decl *D, bool IsWindows) { if (D->hasAttr()) return IsWindows ? CC_X86_64SysV : CC_C; + if (D->hasAttr()) + return CC_PreserveMost; + + if (D->hasAttr()) + return CC_PreserveAll; + return CC_C; } diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 85fbc1f34d1..cb45e17077c 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -3760,7 +3760,14 @@ static void handleCallConvAttr(Sema &S, Decl *D, const AttributeList &Attr) { IntelOclBiccAttr(Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); return; - + case AttributeList::AT_PreserveMost: + D->addAttr(::new (S.Context) PreserveMostAttr( + Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); + return; + case AttributeList::AT_PreserveAll: + D->addAttr(::new (S.Context) PreserveAllAttr( + Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); + return; default: llvm_unreachable("unexpected attribute kind"); } @@ -3812,6 +3819,8 @@ bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC, return true; } case AttributeList::AT_IntelOclBicc: CC = CC_IntelOclBicc; break; + case AttributeList::AT_PreserveMost: CC = CC_PreserveMost; break; + case AttributeList::AT_PreserveAll: CC = CC_PreserveAll; break; default: llvm_unreachable("unexpected attribute kind"); } @@ -5784,6 +5793,8 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_SysVABI: case AttributeList::AT_Pcs: case AttributeList::AT_IntelOclBicc: + case AttributeList::AT_PreserveMost: + case AttributeList::AT_PreserveAll: handleCallConvAttr(S, D, Attr); break; case AttributeList::AT_OpenCLKernel: diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 844be540e0a..4e37fc9f0cb 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -113,7 +113,9 @@ static void diagnoseBadTypeAttribute(Sema &S, const AttributeList &attr, case AttributeList::AT_SysVABI: \ case AttributeList::AT_Regparm: \ case AttributeList::AT_Pcs: \ - case AttributeList::AT_IntelOclBicc + case AttributeList::AT_IntelOclBicc: \ + case AttributeList::AT_PreserveMost: \ + case AttributeList::AT_PreserveAll // Microsoft-specific type qualifiers. #define MS_TYPE_ATTRS_CASELIST \ @@ -4512,6 +4514,10 @@ static AttributeList::Kind getAttrListKind(AttributedType::Kind kind) { return AttributeList::AT_MSABI; case AttributedType::attr_sysv_abi: return AttributeList::AT_SysVABI; + case AttributedType::attr_preserve_most: + return AttributeList::AT_PreserveMost; + case AttributedType::attr_preserve_all: + return AttributeList::AT_PreserveAll; case AttributedType::attr_ptr32: return AttributeList::AT_Ptr32; case AttributedType::attr_ptr64: @@ -5847,6 +5853,10 @@ static AttributedType::Kind getCCTypeAttrKind(AttributeList &Attr) { return AttributedType::attr_ms_abi; case AttributeList::AT_SysVABI: return AttributedType::attr_sysv_abi; + case AttributeList::AT_PreserveMost: + return AttributedType::attr_preserve_most; + case AttributeList::AT_PreserveAll: + return AttributedType::attr_preserve_all; } llvm_unreachable("unexpected attribute kind!"); } diff --git a/test/CodeGen/preserve-call-conv.c b/test/CodeGen/preserve-call-conv.c new file mode 100644 index 00000000000..6e91a8489b4 --- /dev/null +++ b/test/CodeGen/preserve-call-conv.c @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm < %s | FileCheck %s +// RUN: %clang_cc1 -triple arm64-unknown-unknown -emit-llvm < %s | FileCheck %s + +// Check that the preserve_most calling convention attribute at the source level +// is lowered to the corresponding calling convention attrribute at the LLVM IR +// level. +void foo() __attribute__((preserve_most)) { + // CHECK-LABEL: define preserve_mostcc void @foo() +} + +// Check that the preserve_most calling convention attribute at the source level +// is lowered to the corresponding calling convention attrribute at the LLVM IR +// level. +void boo() __attribute__((preserve_all)) { + // CHECK-LABEL: define preserve_allcc void @boo() +} + diff --git a/test/Sema/preserve-call-conv.c b/test/Sema/preserve-call-conv.c new file mode 100644 index 00000000000..f258f45ac58 --- /dev/null +++ b/test/Sema/preserve-call-conv.c @@ -0,0 +1,35 @@ +// RUN: %clang_cc1 %s -fsyntax-only -triple x86_64-unknown-unknown -verify +// RUN: %clang_cc1 %s -fsyntax-only -triple arm64-unknown-unknown -verify +typedef void typedef_fun_t(int); + +void __attribute__((preserve_most)) foo(void *ptr) { +} + +void __attribute__((preserve_most(1))) foo1(void *ptr) { // expected-error {{'preserve_most' attribute takes no arguments}} +} + +void (__attribute__((preserve_most)) *pfoo1)(void *) = foo; + +void (__attribute__((cdecl)) *pfoo2)(void *) = foo; // expected-warning {{incompatible pointer types initializing 'void (*)(void *) __attribute__((cdecl))' with an expression of type 'void (void *) __attribute__((preserve_most))'}} +void (*pfoo3)(void *) = foo; // expected-warning {{incompatible pointer types initializing 'void (*)(void *)' with an expression of type 'void (void *) __attribute__((preserve_most))'}} + +typedef_fun_t typedef_fun_foo; // expected-note {{previous declaration is here}} +void __attribute__((preserve_most)) typedef_fun_foo(int x) { } // expected-error {{function declared 'preserve_most' here was previously declared without calling convention}} + +struct type_test_foo {} __attribute__((preserve_most)); // expected-warning {{'preserve_most' attribute only applies to functions and methods}} + +void __attribute__((preserve_all)) boo(void *ptr) { +} + +void __attribute__((preserve_all(1))) boo1(void *ptr) { // expected-error {{'preserve_all' attribute takes no arguments}} +} + +void (__attribute__((preserve_all)) *pboo1)(void *) = boo; + +void (__attribute__((cdecl)) *pboo2)(void *) = boo; // expected-warning {{incompatible pointer types initializing 'void (*)(void *) __attribute__((cdecl))' with an expression of type 'void (void *) __attribute__((preserve_all))'}} +void (*pboo3)(void *) = boo; // expected-warning {{incompatible pointer types initializing 'void (*)(void *)' with an expression of type 'void (void *) __attribute__((preserve_all))'}} + +typedef_fun_t typedef_fun_boo; // expected-note {{previous declaration is here}} +void __attribute__((preserve_all)) typedef_fun_boo(int x) { } // expected-error {{function declared 'preserve_all' here was previously declared without calling convention}} + +struct type_test_boo {} __attribute__((preserve_all)); // expected-warning {{'preserve_all' attribute only applies to functions and methods}} diff --git a/tools/libclang/CXType.cpp b/tools/libclang/CXType.cpp index 44bb631f786..d6d5daa0f9d 100644 --- a/tools/libclang/CXType.cpp +++ b/tools/libclang/CXType.cpp @@ -533,6 +533,8 @@ CXCallingConv clang_getFunctionTypeCallingConv(CXType X) { TCALLINGCONV(AAPCS); TCALLINGCONV(AAPCS_VFP); TCALLINGCONV(IntelOclBicc); + TCALLINGCONV(PreserveMost); + TCALLINGCONV(PreserveAll); case CC_SpirFunction: return CXCallingConv_Unexposed; case CC_SpirKernel: return CXCallingConv_Unexposed; break; From 6eae12416ece8bece7ab971deb1b3bf70544f324 Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Wed, 30 Mar 2016 15:50:56 -0700 Subject: [PATCH 396/742] [Import as member] Module map attribute swift_infer_import_as_member Introduces a new module map attribute, allowing a module to opt-into import-as-member inference. This allows the provider of the APIs to use inference, rather than require building with a flag. --- include/clang/Basic/Module.h | 3 +++ include/clang/Lex/ModuleMap.h | 3 +++ lib/Basic/Module.cpp | 2 ++ lib/Lex/ModuleMap.cpp | 9 ++++++++- test/Modules/Inputs/swift_name/module.modulemap | 2 ++ test/Modules/infer_swift_name.m | 6 ++++++ 6 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 test/Modules/Inputs/swift_name/module.modulemap create mode 100644 test/Modules/infer_swift_name.m diff --git a/include/clang/Basic/Module.h b/include/clang/Basic/Module.h index c95968d9a75..f8bdf0618fe 100644 --- a/include/clang/Basic/Module.h +++ b/include/clang/Basic/Module.h @@ -183,6 +183,9 @@ class Module { /// \brief Whether this is an inferred submodule (module * { ... }). unsigned IsInferred : 1; + /// \brief Whether this is a module who has its swift_names inferred. + unsigned IsSwiftInferImportAsMember : 1; + /// \brief Whether we should infer submodules for this module based on /// the headers. /// diff --git a/include/clang/Lex/ModuleMap.h b/include/clang/Lex/ModuleMap.h index 6d171b24f24..4d96644beae 100644 --- a/include/clang/Lex/ModuleMap.h +++ b/include/clang/Lex/ModuleMap.h @@ -176,6 +176,9 @@ class ModuleMap { /// \brief Whether this is an exhaustive set of configuration macros. unsigned IsExhaustive : 1; + + /// \brief Whether this is a module who has its swift_names inferred. + unsigned IsSwiftInferImportAsMember : 1; }; /// \brief A directory for which framework modules can be inferred. diff --git a/lib/Basic/Module.cpp b/lib/Basic/Module.cpp index b4f8d252d21..9e5b5807516 100644 --- a/lib/Basic/Module.cpp +++ b/lib/Basic/Module.cpp @@ -337,6 +337,8 @@ void Module::print(raw_ostream &OS, unsigned Indent) const { OS << " [system]"; if (IsExternC) OS << " [extern_c]"; + if (IsSwiftInferImportAsMember) + OS << " [swift_infer_import_as_member]"; } OS << " {\n"; diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp index ac766a97c9e..58aa6ee1902 100644 --- a/lib/Lex/ModuleMap.cpp +++ b/lib/Lex/ModuleMap.cpp @@ -1306,7 +1306,9 @@ namespace { /// \brief The 'extern_c' attribute. AT_extern_c, /// \brief The 'exhaustive' attribute. - AT_exhaustive + AT_exhaustive, + // \brief The 'swift_infer_import_as_member' attribute. + AT_swift_infer_import_as_member, }; } @@ -2379,6 +2381,7 @@ bool ModuleMapParser::parseOptionalAttributes(Attributes &Attrs) { .Case("exhaustive", AT_exhaustive) .Case("extern_c", AT_extern_c) .Case("system", AT_system) + .Case("swift_infer_import_as_member", AT_swift_infer_import_as_member) .Default(AT_unknown); switch (Attribute) { case AT_unknown: @@ -2394,6 +2397,10 @@ bool ModuleMapParser::parseOptionalAttributes(Attributes &Attrs) { Attrs.IsExternC = true; break; + case AT_swift_infer_import_as_member: + Attrs.IsSwiftInferImportAsMember = true; + break; + case AT_exhaustive: Attrs.IsExhaustive = true; break; diff --git a/test/Modules/Inputs/swift_name/module.modulemap b/test/Modules/Inputs/swift_name/module.modulemap new file mode 100644 index 00000000000..b7ec6b15988 --- /dev/null +++ b/test/Modules/Inputs/swift_name/module.modulemap @@ -0,0 +1,2 @@ +module SwiftNameInferred [swift_infer_import_as_member] { +} \ No newline at end of file diff --git a/test/Modules/infer_swift_name.m b/test/Modules/infer_swift_name.m new file mode 100644 index 00000000000..d4b4a5d4979 --- /dev/null +++ b/test/Modules/infer_swift_name.m @@ -0,0 +1,6 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -fmodules-cache-path=%t -fmodules -fimplicit-module-maps -I %S/Inputs/swift_name %s -verify +// REQUIRES: shell + +@import SwiftNameInferred; // ok +@import SwiftName; // expected-error{{module 'SwiftName' not found}} From 6e75259d0cb2327ceefc544b5381105e50ce196d Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Wed, 30 Mar 2016 17:36:32 -0700 Subject: [PATCH 397/742] [Import as member] APINotes for SwiftInferImportAsMember. Adds APINotes support for adding the swift_infer_import_as_member module map attribute. --- include/clang/APINotes/APINotesReader.h | 3 +++ include/clang/APINotes/APINotesWriter.h | 3 +++ include/clang/APINotes/Types.h | 5 +++++ lib/APINotes/APINotesFormat.h | 8 +++++++- lib/APINotes/APINotesReader.cpp | 10 ++++++++++ lib/APINotes/APINotesWriter.cpp | 12 ++++++++++++ lib/APINotes/APINotesYAMLCompiler.cpp | 11 +++++++++++ test/APINotes/Inputs/Headers/APINotes.apinotes | 1 + test/APINotes/Inputs/roundtrip.apinotes | 1 + 9 files changed, 53 insertions(+), 1 deletion(-) diff --git a/include/clang/APINotes/APINotesReader.h b/include/clang/APINotes/APINotesReader.h index 839e1dc5ee9..29dcc1f56f9 100644 --- a/include/clang/APINotes/APINotesReader.h +++ b/include/clang/APINotes/APINotesReader.h @@ -50,6 +50,9 @@ class APINotesReader { /// notes. StringRef getModuleName() const; + /// Retrieve the module options + ModuleOptions getModuleOptions() const; + /// Look for information regarding the given Objective-C class. /// /// \param name The name of the class we're looking for. diff --git a/include/clang/APINotes/APINotesWriter.h b/include/clang/APINotes/APINotesWriter.h index 66dc8ffebba..aa41756b2e7 100644 --- a/include/clang/APINotes/APINotesWriter.h +++ b/include/clang/APINotes/APINotesWriter.h @@ -107,6 +107,9 @@ class APINotesWriter { /// \param name The name of this typedef. /// \param info Information about this typedef. void addTypedef(StringRef name, const TypedefInfo &info); + + /// Add module options + void addModuleOptions(ModuleOptions opts); }; } // end namespace api_notes diff --git a/include/clang/APINotes/Types.h b/include/clang/APINotes/Types.h index 2fc4ee8fe73..7b4d0647ef2 100644 --- a/include/clang/APINotes/Types.h +++ b/include/clang/APINotes/Types.h @@ -474,6 +474,11 @@ class TypedefInfo : public CommonTypeInfo { TypedefInfo() : CommonTypeInfo() { } }; +/// Descripts a series of options for a module +struct ModuleOptions { + bool SwiftInferImportAsMember; +}; + } // end namespace api_notes } // end namespace clang diff --git a/lib/APINotes/APINotesFormat.h b/lib/APINotes/APINotesFormat.h index d344a30a661..cae9ca962ae 100644 --- a/lib/APINotes/APINotesFormat.h +++ b/lib/APINotes/APINotesFormat.h @@ -104,7 +104,8 @@ namespace control_block { // VERSION_MAJOR. enum { METADATA = 1, - MODULE_NAME = 2 + MODULE_NAME = 2, + MODULE_OPTIONS = 3 }; using MetadataLayout = BCRecordLayout< @@ -117,6 +118,11 @@ namespace control_block { MODULE_NAME, BCBlob // Module name >; + + using ModuleOptionsLayout = BCRecordLayout< + MODULE_OPTIONS, + BCFixed<1> // SwiftInferImportAsMember + >; } namespace identifier_block { diff --git a/lib/APINotes/APINotesReader.cpp b/lib/APINotes/APINotesReader.cpp index 62acecf3cc8..66a989920f8 100644 --- a/lib/APINotes/APINotesReader.cpp +++ b/lib/APINotes/APINotesReader.cpp @@ -562,6 +562,9 @@ class APINotesReader::Implementation { /// The name of the module that we read from the control block. std::string ModuleName; + /// Various options and attributes for the module + ModuleOptions moduleOptions; + using SerializedIdentifierTable = llvm::OnDiskIterableChainedHashTable; @@ -735,6 +738,9 @@ bool APINotesReader::Implementation::readControlBlock( ModuleName = blobData.str(); break; + case control_block::MODULE_OPTIONS: + moduleOptions = {(scratch.front() & 1) != 0}; + default: // Unknown metadata record, possibly for use by a future version of the // module format. @@ -1440,6 +1446,10 @@ StringRef APINotesReader::getModuleName() const { return Impl.ModuleName; } +ModuleOptions APINotesReader::getModuleOptions() const { + return Impl.moduleOptions; +} + auto APINotesReader::lookupObjCClass(StringRef name) -> Optional> { if (!Impl.ObjCContextTable) diff --git a/lib/APINotes/APINotesWriter.cpp b/lib/APINotes/APINotesWriter.cpp index 80c1f3487b3..425186ec2f7 100644 --- a/lib/APINotes/APINotesWriter.cpp +++ b/lib/APINotes/APINotesWriter.cpp @@ -43,6 +43,8 @@ class APINotesWriter::Implementation { /// The name of the module std::string ModuleName; + bool SwiftInferImportAsMember = false; + /// Information about Objective-C contexts (classes or protocols). /// /// Indexed by the identifier ID and a bit indication whether we're looking @@ -214,6 +216,11 @@ void APINotesWriter::Implementation::writeControlBlock( control_block::ModuleNameLayout moduleName(writer); moduleName.emit(ScratchRecord, ModuleName); + + if (SwiftInferImportAsMember) { + control_block::ModuleOptionsLayout moduleOptions(writer); + moduleOptions.emit(ScratchRecord, SwiftInferImportAsMember); + } } namespace { @@ -1091,3 +1098,8 @@ void APINotesWriter::addTypedef(llvm::StringRef name, const TypedefInfo &info) { assert(!Impl.Typedefs.count(typedefID)); Impl.Typedefs[typedefID] = info; } + +void APINotesWriter::addModuleOptions(ModuleOptions opts) { + Impl.SwiftInferImportAsMember = opts.SwiftInferImportAsMember; +} + diff --git a/lib/APINotes/APINotesYAMLCompiler.cpp b/lib/APINotes/APINotesYAMLCompiler.cpp index 9ad6dd6a085..a9c21535c8e 100644 --- a/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/lib/APINotes/APINotesYAMLCompiler.cpp @@ -250,6 +250,8 @@ namespace { TagsSeq Tags; TypedefsSeq Typedefs; + llvm::Optional SwiftInferImportAsMember; + LLVM_ATTRIBUTE_DEPRECATED( void dump() LLVM_ATTRIBUTE_USED, "only for use within the debugger"); @@ -416,6 +418,7 @@ namespace llvm { io.mapRequired("Name", m.Name); io.mapOptional("Availability", m.Availability.Mode); io.mapOptional("AvailabilityMsg", m.Availability.Msg); + io.mapOptional("SwiftInferImportAsMember", m.SwiftInferImportAsMember); io.mapOptional("Classes", m.Classes); io.mapOptional("Protocols", m.Protocols); io.mapOptional("Functions", m.Functions); @@ -771,6 +774,9 @@ namespace { Writer->addTypedef(t.Name, typedefInfo); } + if (TheModule.SwiftInferImportAsMember) + Writer->addModuleOptions({true}); + if (!ErrorOccured) Writer->writeToStream(OS); @@ -1032,6 +1038,11 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, // Set module name. module.Name = reader->getModuleName(); + // Set module options + auto opts = reader->getModuleOptions(); + if (opts.SwiftInferImportAsMember) + module.SwiftInferImportAsMember = true; + // Sort classes. std::sort(module.Classes.begin(), module.Classes.end(), [](const Class &lhs, const Class &rhs) -> bool { diff --git a/test/APINotes/Inputs/Headers/APINotes.apinotes b/test/APINotes/Inputs/Headers/APINotes.apinotes index a4ddafe2892..08210fc7056 100644 --- a/test/APINotes/Inputs/Headers/APINotes.apinotes +++ b/test/APINotes/Inputs/Headers/APINotes.apinotes @@ -1,4 +1,5 @@ Name: HeaderLib +SwiftInferImportAsMember: true Functions: - Name: custom_realloc NullabilityOfRet: N diff --git a/test/APINotes/Inputs/roundtrip.apinotes b/test/APINotes/Inputs/roundtrip.apinotes index c34907ff67d..8509c79389b 100644 --- a/test/APINotes/Inputs/roundtrip.apinotes +++ b/test/APINotes/Inputs/roundtrip.apinotes @@ -2,6 +2,7 @@ Name: AppKit Availability: available AvailabilityMsg: '' +SwiftInferImportAsMember: true Classes: - Name: NSCell Availability: available From d05e46e25515b6634d7901bedbc00d5e00c14eb0 Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Fri, 1 Apr 2016 11:22:42 -0700 Subject: [PATCH 398/742] [APINotes] Apply ModuleOptions CompilerInstance will now check for ModuleOptions in the current module's APINotes, and apply them if present. --- include/clang/APINotes/APINotesManager.h | 4 ++++ lib/Frontend/CompilerInstance.cpp | 16 ++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/include/clang/APINotes/APINotesManager.h b/include/clang/APINotes/APINotesManager.h index 69752a9c77e..7f540b9c17d 100644 --- a/include/clang/APINotes/APINotesManager.h +++ b/include/clang/APINotes/APINotesManager.h @@ -111,6 +111,10 @@ class APINotesManager { /// Find the API notes reader that corresponds to the given source location. APINotesReader *findAPINotes(SourceLocation Loc); + + APINotesReader *getCurrentModuleReader() { + return CurrentModuleReader.get(); + } }; } // end namespace api_notes diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp index acb983af6a3..cdb9cf20a0b 100644 --- a/lib/Frontend/CompilerInstance.cpp +++ b/lib/Frontend/CompilerInstance.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "clang/Frontend/CompilerInstance.h" +#include "clang/APINotes/APINotesReader.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" @@ -533,10 +534,21 @@ void CompilerInstance::createSema(TranslationUnitKind TUKind, TUKind, CompletionConsumer)); // If we're building a module, notify the API notes manager. - if (!getLangOpts().CurrentModule.empty()) { + StringRef currentModuleName = getLangOpts().CurrentModule; + if (!currentModuleName.empty()) { (void)TheSema->APINotes.loadCurrentModuleAPINotes( - getLangOpts().CurrentModule, + currentModuleName, getAPINotesOpts().ModuleSearchPaths); + // Check for any attributes we should add to the module + if (auto curReader = TheSema->APINotes.getCurrentModuleReader()) { + auto currentModule = getPreprocessor().getCurrentModule(); + assert(currentModule && "how can we have a reader for it?"); + + // swift_infer_import_as_member + if (curReader->getModuleOptions().SwiftInferImportAsMember) { + currentModule->IsSwiftInferImportAsMember = true; + } + } } } From 262574a11eca84faeac5ee9028572f057cde163c Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Fri, 1 Apr 2016 13:34:49 -0700 Subject: [PATCH 399/742] [APINotes] Incorporate Doug's feedback --- lib/APINotes/APINotesFormat.h | 2 +- lib/APINotes/APINotesReader.cpp | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/APINotes/APINotesFormat.h b/lib/APINotes/APINotesFormat.h index cae9ca962ae..0e8ca3368f6 100644 --- a/lib/APINotes/APINotesFormat.h +++ b/lib/APINotes/APINotesFormat.h @@ -35,7 +35,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 10; // enum constants +const uint16_t VERSION_MINOR = 11; // SwiftInferImportAsMember using IdentifierID = Fixnum<31>; using IdentifierIDField = BCVBR<16>; diff --git a/lib/APINotes/APINotesReader.cpp b/lib/APINotes/APINotesReader.cpp index 66a989920f8..d424b39e3aa 100644 --- a/lib/APINotes/APINotesReader.cpp +++ b/lib/APINotes/APINotesReader.cpp @@ -563,7 +563,7 @@ class APINotesReader::Implementation { std::string ModuleName; /// Various options and attributes for the module - ModuleOptions moduleOptions; + ModuleOptions ModuleOpts; using SerializedIdentifierTable = llvm::OnDiskIterableChainedHashTable; @@ -739,7 +739,8 @@ bool APINotesReader::Implementation::readControlBlock( break; case control_block::MODULE_OPTIONS: - moduleOptions = {(scratch.front() & 1) != 0}; + ModuleOpts.SwiftInferImportAsMember = (scratch.front() & 1) != 0; + break; default: // Unknown metadata record, possibly for use by a future version of the @@ -1447,7 +1448,7 @@ StringRef APINotesReader::getModuleName() const { } ModuleOptions APINotesReader::getModuleOptions() const { - return Impl.moduleOptions; + return Impl.ModuleOpts; } auto APINotesReader::lookupObjCClass(StringRef name) From 78be9bd3659479663bed822953a2c0729561264b Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Fri, 1 Apr 2016 22:58:55 +0000 Subject: [PATCH 400/742] [CodeGen] Emit lifetime.end intrinsic after objects are destructed in landing pads. Previously, lifetime.end intrinsics were inserted only on normal control flows. This prevented StackColoring from merging stack slots for objects that were destroyed on the exception handling control flow since it couldn't tell their lifetime ranges were disjoint. This patch fixes code-gen to emit the intrinsic on both control flows. rdar://problem/22181976 Differential Revision: http://reviews.llvm.org/D18196 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265197 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 0a54ccf3d639cd191bf4c46f81a240675500a6e5) --- lib/CodeGen/CGCleanup.cpp | 14 ++++ lib/CodeGen/CGDecl.cpp | 2 +- lib/CodeGen/EHScopeStack.h | 4 +- test/CodeGenCXX/destructors.cpp | 64 +++++++++++++++++++ test/CodeGenCXX/microsoft-abi-eh-cleanups.cpp | 34 ++++++++++ 5 files changed, 114 insertions(+), 4 deletions(-) diff --git a/lib/CodeGen/CGCleanup.cpp b/lib/CodeGen/CGCleanup.cpp index 2678b33f392..95333d0ddac 100644 --- a/lib/CodeGen/CGCleanup.cpp +++ b/lib/CodeGen/CGCleanup.cpp @@ -157,6 +157,20 @@ bool EHScopeStack::containsOnlyLifetimeMarkers( return true; } +bool EHScopeStack::requiresLandingPad() const { + for (stable_iterator si = getInnermostEHScope(); si != stable_end(); ) { + // Skip lifetime markers. + if (auto *cleanup = dyn_cast(&*find(si))) + if (cleanup->isLifetimeMarker()) { + si = cleanup->getEnclosingEHScope(); + continue; + } + return true; + } + + return false; +} + EHScopeStack::stable_iterator EHScopeStack::getInnermostActiveNormalCleanup() const { for (stable_iterator si = getInnermostNormalCleanup(), se = stable_end(); diff --git a/lib/CodeGen/CGDecl.cpp b/lib/CodeGen/CGDecl.cpp index e8a4d552923..098be6135d2 100644 --- a/lib/CodeGen/CGDecl.cpp +++ b/lib/CodeGen/CGDecl.cpp @@ -1381,7 +1381,7 @@ void CodeGenFunction::EmitAutoVarCleanups(const AutoVarEmission &emission) { // Make sure we call @llvm.lifetime.end. This needs to happen // *last*, so the cleanup needs to be pushed *first*. if (emission.useLifetimeMarkers()) { - EHStack.pushCleanup(NormalCleanup, + EHStack.pushCleanup(NormalAndEHCleanup, emission.getAllocatedAddress(), emission.getSizeForLifetimeMarkers()); EHCleanupScope &cleanup = cast(*EHStack.begin()); diff --git a/lib/CodeGen/EHScopeStack.h b/lib/CodeGen/EHScopeStack.h index 85cd1543e5b..8352c75d64f 100644 --- a/lib/CodeGen/EHScopeStack.h +++ b/lib/CodeGen/EHScopeStack.h @@ -341,9 +341,7 @@ class EHScopeStack { /// Determines whether the exception-scopes stack is empty. bool empty() const { return StartOfData == EndOfBuffer; } - bool requiresLandingPad() const { - return InnermostEHScope != stable_end(); - } + bool requiresLandingPad() const; /// Determines whether there are any normal cleanups on the stack. bool hasNormalCleanups() const { diff --git a/test/CodeGenCXX/destructors.cpp b/test/CodeGenCXX/destructors.cpp index 529603142d3..d6aabee58b9 100644 --- a/test/CodeGenCXX/destructors.cpp +++ b/test/CodeGenCXX/destructors.cpp @@ -4,6 +4,9 @@ // RUN: FileCheck --check-prefix=CHECK3 --input-file=%t %s // RUN: FileCheck --check-prefix=CHECK4 --input-file=%t %s // RUN: FileCheck --check-prefix=CHECK5 --input-file=%t %s +// RUN: %clang_cc1 %s -triple x86_64-apple-darwin10 -emit-llvm -o - -fcxx-exceptions -fexceptions -O1 -disable-llvm-optzns -std=c++11 > %t2 +// RUN: FileCheck --check-prefix=CHECK6 --input-file=%t2 %s +// REQUIRES: asserts struct A { int a; @@ -428,3 +431,64 @@ namespace test10 { return true; } } + +#if __cplusplus >= 201103L +namespace test11 { + +// Check that lifetime.end is emitted in the landing pad. + +// CHECK6-LABEL: define void @_ZN6test1115testLifetimeEndEi( +// CHECK6: entry: +// CHECK6: [[T1:%[a-z0-9]+]] = alloca %"struct.test11::S1" +// CHECK6: [[T2:%[a-z0-9]+]] = alloca %"struct.test11::S1" +// CHECK6: [[T3:%[a-z0-9]+]] = alloca %"struct.test11::S1" + +// CHECK6: {{^}}invoke.cont +// CHECK6: call void @_ZN6test112S1D1Ev(%"struct.test11::S1"* [[T1]]) +// CHECK6: [[BC1:%[a-z0-9]+]] = bitcast %"struct.test11::S1"* [[T1]] to i8* +// CHECK6: call void @llvm.lifetime.end(i64 32, i8* [[BC1]]) +// CHECK6: {{^}}lpad +// CHECK6: call void @_ZN6test112S1D1Ev(%"struct.test11::S1"* [[T1]]) +// CHECK6: [[BC2:%[a-z0-9]+]] = bitcast %"struct.test11::S1"* [[T1]] to i8* +// CHECK6: call void @llvm.lifetime.end(i64 32, i8* [[BC2]]) + +// CHECK6: {{^}}invoke.cont +// CHECK6: call void @_ZN6test112S1D1Ev(%"struct.test11::S1"* [[T2]]) +// CHECK6: [[BC3:%[a-z0-9]+]] = bitcast %"struct.test11::S1"* [[T2]] to i8* +// CHECK6: call void @llvm.lifetime.end(i64 32, i8* [[BC3]]) +// CHECK6: {{^}}lpad +// CHECK6: call void @_ZN6test112S1D1Ev(%"struct.test11::S1"* [[T2]]) +// CHECK6: [[BC4:%[a-z0-9]+]] = bitcast %"struct.test11::S1"* [[T2]] to i8* +// CHECK6: call void @llvm.lifetime.end(i64 32, i8* [[BC4]]) + +// CHECK6: {{^}}invoke.cont +// CHECK6: call void @_ZN6test112S1D1Ev(%"struct.test11::S1"* [[T3]]) +// CHECK6: [[BC5:%[a-z0-9]+]] = bitcast %"struct.test11::S1"* [[T3]] to i8* +// CHECK6: call void @llvm.lifetime.end(i64 32, i8* [[BC5]]) +// CHECK6: {{^}}lpad +// CHECK6: call void @_ZN6test112S1D1Ev(%"struct.test11::S1"* [[T3]]) +// CHECK6: [[BC6:%[a-z0-9]+]] = bitcast %"struct.test11::S1"* [[T3]] to i8* +// CHECK6: call void @llvm.lifetime.end(i64 32, i8* [[BC6]]) + + struct S1 { + ~S1(); + int a[8]; + }; + + void func1(S1 &) noexcept(false); + + void testLifetimeEnd(int n) { + if (n < 10) { + S1 t1; + func1(t1); + } else if (n < 100) { + S1 t2; + func1(t2); + } else if (n < 1000) { + S1 t3; + func1(t3); + } + } + +} +#endif diff --git a/test/CodeGenCXX/microsoft-abi-eh-cleanups.cpp b/test/CodeGenCXX/microsoft-abi-eh-cleanups.cpp index bf05c693ec0..3afee1c1f12 100644 --- a/test/CodeGenCXX/microsoft-abi-eh-cleanups.cpp +++ b/test/CodeGenCXX/microsoft-abi-eh-cleanups.cpp @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -std=c++11 -emit-llvm %s -o - -triple=i386-pc-win32 -mconstructor-aliases -fexceptions -fcxx-exceptions -fno-rtti | FileCheck -check-prefix WIN32 %s +// RUN: %clang_cc1 -std=c++11 -emit-llvm -O3 -disable-llvm-optzns %s -o - -triple=i386-pc-win32 -mconstructor-aliases -fexceptions -fcxx-exceptions -fno-rtti | FileCheck -check-prefix WIN32 -check-prefix WIN32-LIFETIME %s struct A { A(); @@ -206,3 +207,36 @@ void f() { // WIN32: cleanuppad // WIN32: call x86_thiscallcc void @"\01??1D@noexcept_false_dtor@@QAE@XZ"(%"struct.noexcept_false_dtor::D"* %{{.*}}) // WIN32: cleanupret + +namespace lifetime_marker { +struct C { + ~C(); +}; +void g(); +void f() { + C c; + g(); +} + +// WIN32-LIFETIME-LABEL: define void @"\01?f@lifetime_marker@@YAXXZ"() +// WIN32-LIFETIME: %[[c:.*]] = alloca %"struct.lifetime_marker::C" +// WIN32-LIFETIME: %[[bc0:.*]] = bitcast %"struct.lifetime_marker::C"* %c to i8* +// WIN32-LIFETIME: call void @llvm.lifetime.start(i64 1, i8* %[[bc0]]) +// WIN32-LIFETIME: invoke void @"\01?g@lifetime_marker@@YAXXZ"() +// WIN32-LIFETIME-NEXT: to label %[[cont:[^ ]*]] unwind label %[[lpad0:[^ ]*]] +// +// WIN32-LIFETIME: [[cont]]: +// WIN32-LIFETIME: call x86_thiscallcc void @"\01??1C@lifetime_marker@@QAE@XZ"({{.*}}) +// WIN32-LIFETIME: %[[bc1:.*]] = bitcast %"struct.lifetime_marker::C"* %[[c]] to i8* +// WIN32-LIFETIME: call void @llvm.lifetime.end(i64 1, i8* %[[bc1]]) +// +// WIN32-LIFETIME: [[lpad0]]: +// WIN32-LIFETIME-NEXT: cleanuppad +// WIN32-LIFETIME: call x86_thiscallcc void @"\01??1C@lifetime_marker@@QAE@XZ"({{.*}}) +// WIN32-LIFETIME: cleanupret {{.*}} unwind label %[[lpad1:[^ ]*]] +// +// WIN32-LIFETIME: [[lpad1]]: +// WIN32-LIFETIME-NEXT: cleanuppad +// WIN32-LIFETIME: %[[bc2:.*]] = bitcast %"struct.lifetime_marker::C"* %[[c]] to i8* +// WIN32-LIFETIME: call void @llvm.lifetime.end(i64 1, i8* %[[bc2]]) +} From 982c537288e2783cb0c398639944ad3890d8c36b Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Fri, 1 Apr 2016 23:23:52 +0000 Subject: [PATCH 401/742] [Objective-C] Introduce objc_runtime_visible attribute. The objc_runtime_visible attribute deals with an odd corner case where a particular Objective-C class is known to the Objective-C runtime (and, therefore, accessible by name) but its symbol has been hidden for some reason. For such classes, teach CodeGen to use objc_lookUpClass to retrieve the Class object, rather than referencing the class symbol directly. Classes annotated with objc_runtime_visible have two major limitations that fall out from places where Objective-C metadata needs to refer to the class (or metaclass) symbol directly: * One cannot implement a subclass of an objc_runtime_visible class. * One cannot implement a category on an objc_runtime_visible class. Implements rdar://problem/25494092. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265201 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/Attr.td | 6 +++ include/clang/Basic/AttrDocs.td | 7 +++ include/clang/Basic/DiagnosticSemaKinds.td | 6 +++ lib/CodeGen/CGObjCMac.cpp | 49 ++++++++++++++++++++ lib/Sema/SemaDeclAttr.cpp | 4 +- lib/Sema/SemaDeclObjC.cpp | 17 +++++++ test/CodeGenObjC/attr-objc-runtime-visible.m | 19 ++++++++ test/SemaObjC/attr-objc-runtime-visible.m | 19 ++++++++ 8 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 test/CodeGenObjC/attr-objc-runtime-visible.m create mode 100644 test/SemaObjC/attr-objc-runtime-visible.m diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index cec4b9b2780..b75035a6e9b 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -1233,6 +1233,12 @@ def ObjCRuntimeName : Attr { let Documentation = [ObjCRuntimeNameDocs]; } +def ObjCRuntimeVisible : Attr { + let Spellings = [GNU<"objc_runtime_visible">]; + let Subjects = SubjectList<[ObjCInterface], ErrorDiag>; + let Documentation = [ObjCRuntimeVisibleDocs]; +} + def ObjCBoxable : Attr { let Spellings = [GNU<"objc_boxable">]; let Subjects = SubjectList<[Record], ErrorDiag, "ExpectedStructOrUnion">; diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td index d6c901ae412..d0562615f51 100644 --- a/include/clang/Basic/AttrDocs.td +++ b/include/clang/Basic/AttrDocs.td @@ -589,6 +589,13 @@ can only be placed before an @protocol or @interface declaration: }]; } +def ObjCRuntimeVisibleDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +This attribute specifies that the Objective-C class to which it applies is visible to the Objective-C runtime but not to the linker. Classes annotated with this attribute cannot be subclassed and cannot have categories defined for them. + }]; +} + def ObjCBoxableDocs : Documentation { let Category = DocCatFunction; let Content = [{ diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index dd9fb576255..cd880f03a89 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -728,6 +728,12 @@ def err_restricted_superclass_mismatch : Error< def warn_objc_root_class_missing : Warning< "class %0 defined without specifying a base class">, InGroup; +def err_objc_runtime_visible_category : Error< + "cannot implement a category for class %0 that is only visible via the " + "Objective-C runtime">; +def err_objc_runtime_visible_subclass : Error< + "cannot implement subclass %0 of a superclass %1 that is only visible via the " + "Objective-C runtime">; def note_objc_needs_superclass : Note< "add a super class to fix this problem">; def warn_dup_category_def : Warning< diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index fdbc983aa5b..7e3b07a5093 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/lib/CodeGen/CGObjCMac.cpp @@ -356,6 +356,20 @@ class ObjCCommonTypesHelper { return CGM.CreateRuntimeFunction(FTy, "objc_enumerationMutation"); } + llvm::Constant *getLookUpClassFn() { + CodeGen::CodeGenTypes &Types = CGM.getTypes(); + ASTContext &Ctx = CGM.getContext(); + // Class objc_lookUpClass (const char *) + SmallVector Params; + Params.push_back( + Ctx.getCanonicalType(Ctx.getPointerType(Ctx.CharTy.withConst()))); + llvm::FunctionType *FTy = + Types.GetFunctionType(Types.arrangeBuiltinFunctionDeclaration( + Ctx.getCanonicalType(Ctx.getObjCClassType()), + Params)); + return CGM.CreateRuntimeFunction(FTy, "objc_lookUpClass"); + } + /// GcReadWeakFn -- LLVM objc_read_weak (id *src) function. llvm::Constant *getGcReadWeakFn() { // id objc_read_weak (id *) @@ -990,6 +1004,12 @@ class CGObjCCommonMac : public CodeGen::CGObjCRuntime { /// defined. The return value has type ProtocolPtrTy. llvm::Constant *GetProtocolRef(const ObjCProtocolDecl *PD); + /// Return a reference to the given Class using runtime calls rather than + /// by a symbol reference. + llvm::Value *EmitClassRefViaRuntime(CodeGenFunction &CGF, + const ObjCInterfaceDecl *ID, + ObjCCommonTypesHelper &ObjCTypes); + public: /// CreateMetadataVar - Create a global variable with internal /// linkage for use by the Objective-C runtime. @@ -2682,6 +2702,25 @@ llvm::Constant *CGObjCCommonMac::GetProtocolRef(const ObjCProtocolDecl *PD) { return GetOrEmitProtocolRef(PD); } +llvm::Value *CGObjCCommonMac::EmitClassRefViaRuntime( + CodeGenFunction &CGF, + const ObjCInterfaceDecl *ID, + ObjCCommonTypesHelper &ObjCTypes) { + llvm::Constant *lookUpClassFn = ObjCTypes.getLookUpClassFn(); + + llvm::Value *className = + CGF.CGM.GetAddrOfConstantCString(ID->getObjCRuntimeNameAsString()) + .getPointer(); + ASTContext &ctx = CGF.CGM.getContext(); + className = + CGF.Builder.CreateBitCast(className, + CGF.ConvertType( + ctx.getPointerType(ctx.CharTy.withConst()))); + llvm::CallInst *call = CGF.Builder.CreateCall(lookUpClassFn, className); + call->setDoesNotThrow(); + return call; +} + /* // Objective-C 1.0 extensions struct _objc_protocol { @@ -4642,6 +4681,11 @@ llvm::Value *CGObjCMac::EmitClassRefFromId(CodeGenFunction &CGF, llvm::Value *CGObjCMac::EmitClassRef(CodeGenFunction &CGF, const ObjCInterfaceDecl *ID) { + // If the class has the objc_runtime_visible attribute, we need to + // use the Objective-C runtime to get the class. + if (ID->hasAttr()) + return EmitClassRefViaRuntime(CGF, ID, ObjCTypes); + return EmitClassRefFromId(CGF, ID->getIdentifier()); } @@ -6902,6 +6946,11 @@ llvm::Value *CGObjCNonFragileABIMac::EmitClassRefFromId(CodeGenFunction &CGF, llvm::Value *CGObjCNonFragileABIMac::EmitClassRef(CodeGenFunction &CGF, const ObjCInterfaceDecl *ID) { + // If the class has the objc_runtime_visible attribute, we need to + // use the Objective-C runtime to get the class. + if (ID->hasAttr()) + return EmitClassRefViaRuntime(CGF, ID, ObjCTypes); + return EmitClassRefFromId(CGF, ID->getIdentifier(), ID->isWeakImported(), ID); } diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index cb45e17077c..9f03accbf0b 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -5630,7 +5630,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_ObjCRuntimeName: handleObjCRuntimeName(S, D, Attr); break; - + case AttributeList::AT_ObjCRuntimeVisible: + handleSimpleAttribute(S, D, Attr); + break; case AttributeList::AT_ObjCBoxable: handleObjCBoxable(S, D, Attr); break; diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index e5db79f18e3..915e31cb77e 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -1836,6 +1836,13 @@ Decl *Sema::ActOnStartCategoryImplementation( if (IDecl) DiagnoseUseOfDecl(IDecl, ClassLoc); + // If the interface has the objc_runtime_visible attribute, we + // cannot implement a category for it. + if (IDecl && IDecl->hasAttr()) { + Diag(ClassLoc, diag::err_objc_runtime_visible_category) + << IDecl->getDeclName(); + } + /// Check that CatName, category name, is not used in another implementation. if (CatIDecl) { if (CatIDecl->getImplementation()) { @@ -1973,6 +1980,16 @@ Decl *Sema::ActOnStartClassImplementation( dyn_cast(IDecl), IMPDecl->getLocation(), 1); } + + // If the superclass has the objc_runtime_visible attribute, we + // cannot implement a subclass of it. + if (IDecl->getSuperClass() && + IDecl->getSuperClass()->hasAttr()) { + Diag(ClassLoc, diag::err_objc_runtime_visible_subclass) + << IDecl->getDeclName() + << IDecl->getSuperClass()->getDeclName(); + } + return ActOnObjCContainerStartDefinition(IMPDecl); } diff --git a/test/CodeGenObjC/attr-objc-runtime-visible.m b/test/CodeGenObjC/attr-objc-runtime-visible.m new file mode 100644 index 00000000000..6e224e71890 --- /dev/null +++ b/test/CodeGenObjC/attr-objc-runtime-visible.m @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin -fobjc-runtime=macosx-10.9.0 -emit-llvm %s -o - | FileCheck %s + +// RUN: %clang_cc1 -triple i386-apple-darwin -fobjc-runtime=macosx-fragile-10.9.0 -emit-llvm %s -o - | FileCheck %s + +@interface Root ++(Class)class; +@end + +__attribute__((objc_runtime_visible)) +__attribute__((objc_runtime_name("MyRuntimeVisibleClass"))) +@interface A : Root +@end + +// CHECK: [[CLASSNAME:@.*]] = private unnamed_addr constant [22 x i8] c"MyRuntimeVisibleClass +// CHECK: define i8* @getClass() #0 { +Class getClass(void) { + // CHECK: call i8* @objc_lookUpClass(i8* getelementptr inbounds ([22 x i8], [22 x i8]* [[CLASSNAME]], i32 0, i32 0)) #2 + return [A class]; +} diff --git a/test/SemaObjC/attr-objc-runtime-visible.m b/test/SemaObjC/attr-objc-runtime-visible.m new file mode 100644 index 00000000000..b5ec809ff2f --- /dev/null +++ b/test/SemaObjC/attr-objc-runtime-visible.m @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -verify -fsyntax-only %s + +__attribute__((objc_runtime_visible)) +@interface A +@end + +@interface A(X) +@end + +@implementation A(X) // expected-error{{cannot implement a category for class 'A' that is only visible via the Objective-C runtime}} +@end + +@interface B : A +@end + +@implementation B // expected-error{{cannot implement subclass 'B' of a superclass 'A' that is only visible via the Objective-C runtime}} +@end + + From 0ec756b1458b9bd965f0e191bfd084f508b303f4 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 1 Apr 2016 23:16:57 -0700 Subject: [PATCH 402/742] Fix a bad cherry-pick --- lib/CodeGen/CGObjCMac.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index 7e3b07a5093..7678f7b4b20 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/lib/CodeGen/CGObjCMac.cpp @@ -364,9 +364,11 @@ class ObjCCommonTypesHelper { Params.push_back( Ctx.getCanonicalType(Ctx.getPointerType(Ctx.CharTy.withConst()))); llvm::FunctionType *FTy = - Types.GetFunctionType(Types.arrangeBuiltinFunctionDeclaration( + Types.GetFunctionType(Types.arrangeLLVMFunctionInfo( Ctx.getCanonicalType(Ctx.getObjCClassType()), - Params)); + false, false, Params, + FunctionType::ExtInfo(), + RequiredArgs::All)); return CGM.CreateRuntimeFunction(FTy, "objc_lookUpClass"); } From ddb622a12f92b43c7e278615ce9ca2a1d92ccc2c Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Fri, 1 Apr 2016 23:23:52 +0000 Subject: [PATCH 403/742] [Objective-C] Introduce objc_runtime_visible attribute. The objc_runtime_visible attribute deals with an odd corner case where a particular Objective-C class is known to the Objective-C runtime (and, therefore, accessible by name) but its symbol has been hidden for some reason. For such classes, teach CodeGen to use objc_lookUpClass to retrieve the Class object, rather than referencing the class symbol directly. Classes annotated with objc_runtime_visible have two major limitations that fall out from places where Objective-C metadata needs to refer to the class (or metaclass) symbol directly: * One cannot implement a subclass of an objc_runtime_visible class. * One cannot implement a category on an objc_runtime_visible class. Implements rdar://problem/25494092. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265201 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/Attr.td | 6 +++ include/clang/Basic/AttrDocs.td | 7 +++ include/clang/Basic/DiagnosticSemaKinds.td | 6 +++ lib/CodeGen/CGObjCMac.cpp | 49 ++++++++++++++++++++ lib/Sema/SemaDeclAttr.cpp | 4 +- lib/Sema/SemaDeclObjC.cpp | 17 +++++++ test/CodeGenObjC/attr-objc-runtime-visible.m | 19 ++++++++ test/SemaObjC/attr-objc-runtime-visible.m | 19 ++++++++ 8 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 test/CodeGenObjC/attr-objc-runtime-visible.m create mode 100644 test/SemaObjC/attr-objc-runtime-visible.m diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 4e1d30afa97..06da89888ab 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -1237,6 +1237,12 @@ def ObjCRuntimeName : Attr { let Documentation = [ObjCRuntimeNameDocs]; } +def ObjCRuntimeVisible : Attr { + let Spellings = [GNU<"objc_runtime_visible">]; + let Subjects = SubjectList<[ObjCInterface], ErrorDiag>; + let Documentation = [ObjCRuntimeVisibleDocs]; +} + def ObjCBoxable : Attr { let Spellings = [GNU<"objc_boxable">]; let Subjects = SubjectList<[Record], ErrorDiag, "ExpectedStructOrUnion">; diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td index 45edb850160..a192c8a4a08 100644 --- a/include/clang/Basic/AttrDocs.td +++ b/include/clang/Basic/AttrDocs.td @@ -589,6 +589,13 @@ can only be placed before an @protocol or @interface declaration: }]; } +def ObjCRuntimeVisibleDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +This attribute specifies that the Objective-C class to which it applies is visible to the Objective-C runtime but not to the linker. Classes annotated with this attribute cannot be subclassed and cannot have categories defined for them. + }]; +} + def ObjCBoxableDocs : Documentation { let Category = DocCatFunction; let Content = [{ diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index afc1a9553a7..4794b4529a4 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -728,6 +728,12 @@ def err_restricted_superclass_mismatch : Error< def warn_objc_root_class_missing : Warning< "class %0 defined without specifying a base class">, InGroup; +def err_objc_runtime_visible_category : Error< + "cannot implement a category for class %0 that is only visible via the " + "Objective-C runtime">; +def err_objc_runtime_visible_subclass : Error< + "cannot implement subclass %0 of a superclass %1 that is only visible via the " + "Objective-C runtime">; def note_objc_needs_superclass : Note< "add a super class to fix this problem">; def warn_dup_category_def : Warning< diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index 0d641377ca4..c491a7e6b7c 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/lib/CodeGen/CGObjCMac.cpp @@ -356,6 +356,20 @@ class ObjCCommonTypesHelper { return CGM.CreateRuntimeFunction(FTy, "objc_enumerationMutation"); } + llvm::Constant *getLookUpClassFn() { + CodeGen::CodeGenTypes &Types = CGM.getTypes(); + ASTContext &Ctx = CGM.getContext(); + // Class objc_lookUpClass (const char *) + SmallVector Params; + Params.push_back( + Ctx.getCanonicalType(Ctx.getPointerType(Ctx.CharTy.withConst()))); + llvm::FunctionType *FTy = + Types.GetFunctionType(Types.arrangeBuiltinFunctionDeclaration( + Ctx.getCanonicalType(Ctx.getObjCClassType()), + Params)); + return CGM.CreateRuntimeFunction(FTy, "objc_lookUpClass"); + } + /// GcReadWeakFn -- LLVM objc_read_weak (id *src) function. llvm::Constant *getGcReadWeakFn() { // id objc_read_weak (id *) @@ -990,6 +1004,12 @@ class CGObjCCommonMac : public CodeGen::CGObjCRuntime { /// defined. The return value has type ProtocolPtrTy. llvm::Constant *GetProtocolRef(const ObjCProtocolDecl *PD); + /// Return a reference to the given Class using runtime calls rather than + /// by a symbol reference. + llvm::Value *EmitClassRefViaRuntime(CodeGenFunction &CGF, + const ObjCInterfaceDecl *ID, + ObjCCommonTypesHelper &ObjCTypes); + public: /// CreateMetadataVar - Create a global variable with internal /// linkage for use by the Objective-C runtime. @@ -2682,6 +2702,25 @@ llvm::Constant *CGObjCCommonMac::GetProtocolRef(const ObjCProtocolDecl *PD) { return GetOrEmitProtocolRef(PD); } +llvm::Value *CGObjCCommonMac::EmitClassRefViaRuntime( + CodeGenFunction &CGF, + const ObjCInterfaceDecl *ID, + ObjCCommonTypesHelper &ObjCTypes) { + llvm::Constant *lookUpClassFn = ObjCTypes.getLookUpClassFn(); + + llvm::Value *className = + CGF.CGM.GetAddrOfConstantCString(ID->getObjCRuntimeNameAsString()) + .getPointer(); + ASTContext &ctx = CGF.CGM.getContext(); + className = + CGF.Builder.CreateBitCast(className, + CGF.ConvertType( + ctx.getPointerType(ctx.CharTy.withConst()))); + llvm::CallInst *call = CGF.Builder.CreateCall(lookUpClassFn, className); + call->setDoesNotThrow(); + return call; +} + /* // Objective-C 1.0 extensions struct _objc_protocol { @@ -4644,6 +4683,11 @@ llvm::Value *CGObjCMac::EmitClassRefFromId(CodeGenFunction &CGF, llvm::Value *CGObjCMac::EmitClassRef(CodeGenFunction &CGF, const ObjCInterfaceDecl *ID) { + // If the class has the objc_runtime_visible attribute, we need to + // use the Objective-C runtime to get the class. + if (ID->hasAttr()) + return EmitClassRefViaRuntime(CGF, ID, ObjCTypes); + return EmitClassRefFromId(CGF, ID->getIdentifier()); } @@ -6907,6 +6951,11 @@ llvm::Value *CGObjCNonFragileABIMac::EmitClassRefFromId(CodeGenFunction &CGF, llvm::Value *CGObjCNonFragileABIMac::EmitClassRef(CodeGenFunction &CGF, const ObjCInterfaceDecl *ID) { + // If the class has the objc_runtime_visible attribute, we need to + // use the Objective-C runtime to get the class. + if (ID->hasAttr()) + return EmitClassRefViaRuntime(CGF, ID, ObjCTypes); + return EmitClassRefFromId(CGF, ID->getIdentifier(), ID->isWeakImported(), ID); } diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index fdf52d76f9d..a4911815f5b 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -5658,7 +5658,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_ObjCRuntimeName: handleObjCRuntimeName(S, D, Attr); break; - + case AttributeList::AT_ObjCRuntimeVisible: + handleSimpleAttribute(S, D, Attr); + break; case AttributeList::AT_ObjCBoxable: handleObjCBoxable(S, D, Attr); break; diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index ff90c9e3a96..c056284f545 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -1836,6 +1836,13 @@ Decl *Sema::ActOnStartCategoryImplementation( if (IDecl) DiagnoseUseOfDecl(IDecl, ClassLoc); + // If the interface has the objc_runtime_visible attribute, we + // cannot implement a category for it. + if (IDecl && IDecl->hasAttr()) { + Diag(ClassLoc, diag::err_objc_runtime_visible_category) + << IDecl->getDeclName(); + } + /// Check that CatName, category name, is not used in another implementation. if (CatIDecl) { if (CatIDecl->getImplementation()) { @@ -1973,6 +1980,16 @@ Decl *Sema::ActOnStartClassImplementation( dyn_cast(IDecl), IMPDecl->getLocation(), 1); } + + // If the superclass has the objc_runtime_visible attribute, we + // cannot implement a subclass of it. + if (IDecl->getSuperClass() && + IDecl->getSuperClass()->hasAttr()) { + Diag(ClassLoc, diag::err_objc_runtime_visible_subclass) + << IDecl->getDeclName() + << IDecl->getSuperClass()->getDeclName(); + } + return ActOnObjCContainerStartDefinition(IMPDecl); } diff --git a/test/CodeGenObjC/attr-objc-runtime-visible.m b/test/CodeGenObjC/attr-objc-runtime-visible.m new file mode 100644 index 00000000000..6e224e71890 --- /dev/null +++ b/test/CodeGenObjC/attr-objc-runtime-visible.m @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin -fobjc-runtime=macosx-10.9.0 -emit-llvm %s -o - | FileCheck %s + +// RUN: %clang_cc1 -triple i386-apple-darwin -fobjc-runtime=macosx-fragile-10.9.0 -emit-llvm %s -o - | FileCheck %s + +@interface Root ++(Class)class; +@end + +__attribute__((objc_runtime_visible)) +__attribute__((objc_runtime_name("MyRuntimeVisibleClass"))) +@interface A : Root +@end + +// CHECK: [[CLASSNAME:@.*]] = private unnamed_addr constant [22 x i8] c"MyRuntimeVisibleClass +// CHECK: define i8* @getClass() #0 { +Class getClass(void) { + // CHECK: call i8* @objc_lookUpClass(i8* getelementptr inbounds ([22 x i8], [22 x i8]* [[CLASSNAME]], i32 0, i32 0)) #2 + return [A class]; +} diff --git a/test/SemaObjC/attr-objc-runtime-visible.m b/test/SemaObjC/attr-objc-runtime-visible.m new file mode 100644 index 00000000000..b5ec809ff2f --- /dev/null +++ b/test/SemaObjC/attr-objc-runtime-visible.m @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -verify -fsyntax-only %s + +__attribute__((objc_runtime_visible)) +@interface A +@end + +@interface A(X) +@end + +@implementation A(X) // expected-error{{cannot implement a category for class 'A' that is only visible via the Objective-C runtime}} +@end + +@interface B : A +@end + +@implementation B // expected-error{{cannot implement subclass 'B' of a superclass 'A' that is only visible via the Objective-C runtime}} +@end + + From af12c7c0d328c66076ab10991c47af9185f3e423 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 1 Apr 2016 23:16:57 -0700 Subject: [PATCH 404/742] Fix a bad cherry-pick --- lib/CodeGen/CGObjCMac.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index c491a7e6b7c..09871e364b6 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/lib/CodeGen/CGObjCMac.cpp @@ -364,9 +364,11 @@ class ObjCCommonTypesHelper { Params.push_back( Ctx.getCanonicalType(Ctx.getPointerType(Ctx.CharTy.withConst()))); llvm::FunctionType *FTy = - Types.GetFunctionType(Types.arrangeBuiltinFunctionDeclaration( + Types.GetFunctionType(Types.arrangeLLVMFunctionInfo( Ctx.getCanonicalType(Ctx.getObjCClassType()), - Params)); + false, false, Params, + FunctionType::ExtInfo(), + RequiredArgs::All)); return CGM.CreateRuntimeFunction(FTy, "objc_lookUpClass"); } From eee116a03389402074ec4c0783c6a34f45a0148b Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Wed, 30 Mar 2016 15:50:56 -0700 Subject: [PATCH 405/742] [Import as member] Module map attribute swift_infer_import_as_member Introduces a new module map attribute, allowing a module to opt-into import-as-member inference. This allows the provider of the APIs to use inference, rather than require building with a flag. --- include/clang/Basic/Module.h | 3 +++ include/clang/Lex/ModuleMap.h | 3 +++ lib/Basic/Module.cpp | 2 ++ lib/Lex/ModuleMap.cpp | 9 ++++++++- test/Modules/Inputs/swift_name/module.modulemap | 2 ++ test/Modules/infer_swift_name.m | 6 ++++++ 6 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 test/Modules/Inputs/swift_name/module.modulemap create mode 100644 test/Modules/infer_swift_name.m diff --git a/include/clang/Basic/Module.h b/include/clang/Basic/Module.h index c95968d9a75..f8bdf0618fe 100644 --- a/include/clang/Basic/Module.h +++ b/include/clang/Basic/Module.h @@ -183,6 +183,9 @@ class Module { /// \brief Whether this is an inferred submodule (module * { ... }). unsigned IsInferred : 1; + /// \brief Whether this is a module who has its swift_names inferred. + unsigned IsSwiftInferImportAsMember : 1; + /// \brief Whether we should infer submodules for this module based on /// the headers. /// diff --git a/include/clang/Lex/ModuleMap.h b/include/clang/Lex/ModuleMap.h index 6d171b24f24..4d96644beae 100644 --- a/include/clang/Lex/ModuleMap.h +++ b/include/clang/Lex/ModuleMap.h @@ -176,6 +176,9 @@ class ModuleMap { /// \brief Whether this is an exhaustive set of configuration macros. unsigned IsExhaustive : 1; + + /// \brief Whether this is a module who has its swift_names inferred. + unsigned IsSwiftInferImportAsMember : 1; }; /// \brief A directory for which framework modules can be inferred. diff --git a/lib/Basic/Module.cpp b/lib/Basic/Module.cpp index 275143fad55..1fd12f15282 100644 --- a/lib/Basic/Module.cpp +++ b/lib/Basic/Module.cpp @@ -337,6 +337,8 @@ void Module::print(raw_ostream &OS, unsigned Indent) const { OS << " [system]"; if (IsExternC) OS << " [extern_c]"; + if (IsSwiftInferImportAsMember) + OS << " [swift_infer_import_as_member]"; } OS << " {\n"; diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp index 63a4813aa9f..7cdda38682e 100644 --- a/lib/Lex/ModuleMap.cpp +++ b/lib/Lex/ModuleMap.cpp @@ -1306,7 +1306,9 @@ namespace { /// \brief The 'extern_c' attribute. AT_extern_c, /// \brief The 'exhaustive' attribute. - AT_exhaustive + AT_exhaustive, + // \brief The 'swift_infer_import_as_member' attribute. + AT_swift_infer_import_as_member, }; } @@ -2379,6 +2381,7 @@ bool ModuleMapParser::parseOptionalAttributes(Attributes &Attrs) { .Case("exhaustive", AT_exhaustive) .Case("extern_c", AT_extern_c) .Case("system", AT_system) + .Case("swift_infer_import_as_member", AT_swift_infer_import_as_member) .Default(AT_unknown); switch (Attribute) { case AT_unknown: @@ -2394,6 +2397,10 @@ bool ModuleMapParser::parseOptionalAttributes(Attributes &Attrs) { Attrs.IsExternC = true; break; + case AT_swift_infer_import_as_member: + Attrs.IsSwiftInferImportAsMember = true; + break; + case AT_exhaustive: Attrs.IsExhaustive = true; break; diff --git a/test/Modules/Inputs/swift_name/module.modulemap b/test/Modules/Inputs/swift_name/module.modulemap new file mode 100644 index 00000000000..b7ec6b15988 --- /dev/null +++ b/test/Modules/Inputs/swift_name/module.modulemap @@ -0,0 +1,2 @@ +module SwiftNameInferred [swift_infer_import_as_member] { +} \ No newline at end of file diff --git a/test/Modules/infer_swift_name.m b/test/Modules/infer_swift_name.m new file mode 100644 index 00000000000..d4b4a5d4979 --- /dev/null +++ b/test/Modules/infer_swift_name.m @@ -0,0 +1,6 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -fmodules-cache-path=%t -fmodules -fimplicit-module-maps -I %S/Inputs/swift_name %s -verify +// REQUIRES: shell + +@import SwiftNameInferred; // ok +@import SwiftName; // expected-error{{module 'SwiftName' not found}} From f7dc6a4be33317865b2f5b5cc235c7997a399833 Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Wed, 30 Mar 2016 17:36:32 -0700 Subject: [PATCH 406/742] [Import as member] APINotes for SwiftInferImportAsMember. Adds APINotes support for adding the swift_infer_import_as_member module map attribute. --- include/clang/APINotes/APINotesReader.h | 3 +++ include/clang/APINotes/APINotesWriter.h | 3 +++ include/clang/APINotes/Types.h | 5 +++++ lib/APINotes/APINotesFormat.h | 8 +++++++- lib/APINotes/APINotesReader.cpp | 10 ++++++++++ lib/APINotes/APINotesWriter.cpp | 12 ++++++++++++ lib/APINotes/APINotesYAMLCompiler.cpp | 11 +++++++++++ test/APINotes/Inputs/Headers/APINotes.apinotes | 1 + test/APINotes/Inputs/roundtrip.apinotes | 1 + 9 files changed, 53 insertions(+), 1 deletion(-) diff --git a/include/clang/APINotes/APINotesReader.h b/include/clang/APINotes/APINotesReader.h index 839e1dc5ee9..29dcc1f56f9 100644 --- a/include/clang/APINotes/APINotesReader.h +++ b/include/clang/APINotes/APINotesReader.h @@ -50,6 +50,9 @@ class APINotesReader { /// notes. StringRef getModuleName() const; + /// Retrieve the module options + ModuleOptions getModuleOptions() const; + /// Look for information regarding the given Objective-C class. /// /// \param name The name of the class we're looking for. diff --git a/include/clang/APINotes/APINotesWriter.h b/include/clang/APINotes/APINotesWriter.h index 66dc8ffebba..aa41756b2e7 100644 --- a/include/clang/APINotes/APINotesWriter.h +++ b/include/clang/APINotes/APINotesWriter.h @@ -107,6 +107,9 @@ class APINotesWriter { /// \param name The name of this typedef. /// \param info Information about this typedef. void addTypedef(StringRef name, const TypedefInfo &info); + + /// Add module options + void addModuleOptions(ModuleOptions opts); }; } // end namespace api_notes diff --git a/include/clang/APINotes/Types.h b/include/clang/APINotes/Types.h index 2fc4ee8fe73..7b4d0647ef2 100644 --- a/include/clang/APINotes/Types.h +++ b/include/clang/APINotes/Types.h @@ -474,6 +474,11 @@ class TypedefInfo : public CommonTypeInfo { TypedefInfo() : CommonTypeInfo() { } }; +/// Descripts a series of options for a module +struct ModuleOptions { + bool SwiftInferImportAsMember; +}; + } // end namespace api_notes } // end namespace clang diff --git a/lib/APINotes/APINotesFormat.h b/lib/APINotes/APINotesFormat.h index d344a30a661..cae9ca962ae 100644 --- a/lib/APINotes/APINotesFormat.h +++ b/lib/APINotes/APINotesFormat.h @@ -104,7 +104,8 @@ namespace control_block { // VERSION_MAJOR. enum { METADATA = 1, - MODULE_NAME = 2 + MODULE_NAME = 2, + MODULE_OPTIONS = 3 }; using MetadataLayout = BCRecordLayout< @@ -117,6 +118,11 @@ namespace control_block { MODULE_NAME, BCBlob // Module name >; + + using ModuleOptionsLayout = BCRecordLayout< + MODULE_OPTIONS, + BCFixed<1> // SwiftInferImportAsMember + >; } namespace identifier_block { diff --git a/lib/APINotes/APINotesReader.cpp b/lib/APINotes/APINotesReader.cpp index 62acecf3cc8..66a989920f8 100644 --- a/lib/APINotes/APINotesReader.cpp +++ b/lib/APINotes/APINotesReader.cpp @@ -562,6 +562,9 @@ class APINotesReader::Implementation { /// The name of the module that we read from the control block. std::string ModuleName; + /// Various options and attributes for the module + ModuleOptions moduleOptions; + using SerializedIdentifierTable = llvm::OnDiskIterableChainedHashTable; @@ -735,6 +738,9 @@ bool APINotesReader::Implementation::readControlBlock( ModuleName = blobData.str(); break; + case control_block::MODULE_OPTIONS: + moduleOptions = {(scratch.front() & 1) != 0}; + default: // Unknown metadata record, possibly for use by a future version of the // module format. @@ -1440,6 +1446,10 @@ StringRef APINotesReader::getModuleName() const { return Impl.ModuleName; } +ModuleOptions APINotesReader::getModuleOptions() const { + return Impl.moduleOptions; +} + auto APINotesReader::lookupObjCClass(StringRef name) -> Optional> { if (!Impl.ObjCContextTable) diff --git a/lib/APINotes/APINotesWriter.cpp b/lib/APINotes/APINotesWriter.cpp index 80c1f3487b3..425186ec2f7 100644 --- a/lib/APINotes/APINotesWriter.cpp +++ b/lib/APINotes/APINotesWriter.cpp @@ -43,6 +43,8 @@ class APINotesWriter::Implementation { /// The name of the module std::string ModuleName; + bool SwiftInferImportAsMember = false; + /// Information about Objective-C contexts (classes or protocols). /// /// Indexed by the identifier ID and a bit indication whether we're looking @@ -214,6 +216,11 @@ void APINotesWriter::Implementation::writeControlBlock( control_block::ModuleNameLayout moduleName(writer); moduleName.emit(ScratchRecord, ModuleName); + + if (SwiftInferImportAsMember) { + control_block::ModuleOptionsLayout moduleOptions(writer); + moduleOptions.emit(ScratchRecord, SwiftInferImportAsMember); + } } namespace { @@ -1091,3 +1098,8 @@ void APINotesWriter::addTypedef(llvm::StringRef name, const TypedefInfo &info) { assert(!Impl.Typedefs.count(typedefID)); Impl.Typedefs[typedefID] = info; } + +void APINotesWriter::addModuleOptions(ModuleOptions opts) { + Impl.SwiftInferImportAsMember = opts.SwiftInferImportAsMember; +} + diff --git a/lib/APINotes/APINotesYAMLCompiler.cpp b/lib/APINotes/APINotesYAMLCompiler.cpp index 9ad6dd6a085..a9c21535c8e 100644 --- a/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/lib/APINotes/APINotesYAMLCompiler.cpp @@ -250,6 +250,8 @@ namespace { TagsSeq Tags; TypedefsSeq Typedefs; + llvm::Optional SwiftInferImportAsMember; + LLVM_ATTRIBUTE_DEPRECATED( void dump() LLVM_ATTRIBUTE_USED, "only for use within the debugger"); @@ -416,6 +418,7 @@ namespace llvm { io.mapRequired("Name", m.Name); io.mapOptional("Availability", m.Availability.Mode); io.mapOptional("AvailabilityMsg", m.Availability.Msg); + io.mapOptional("SwiftInferImportAsMember", m.SwiftInferImportAsMember); io.mapOptional("Classes", m.Classes); io.mapOptional("Protocols", m.Protocols); io.mapOptional("Functions", m.Functions); @@ -771,6 +774,9 @@ namespace { Writer->addTypedef(t.Name, typedefInfo); } + if (TheModule.SwiftInferImportAsMember) + Writer->addModuleOptions({true}); + if (!ErrorOccured) Writer->writeToStream(OS); @@ -1032,6 +1038,11 @@ bool api_notes::decompileAPINotes(std::unique_ptr input, // Set module name. module.Name = reader->getModuleName(); + // Set module options + auto opts = reader->getModuleOptions(); + if (opts.SwiftInferImportAsMember) + module.SwiftInferImportAsMember = true; + // Sort classes. std::sort(module.Classes.begin(), module.Classes.end(), [](const Class &lhs, const Class &rhs) -> bool { diff --git a/test/APINotes/Inputs/Headers/APINotes.apinotes b/test/APINotes/Inputs/Headers/APINotes.apinotes index a4ddafe2892..08210fc7056 100644 --- a/test/APINotes/Inputs/Headers/APINotes.apinotes +++ b/test/APINotes/Inputs/Headers/APINotes.apinotes @@ -1,4 +1,5 @@ Name: HeaderLib +SwiftInferImportAsMember: true Functions: - Name: custom_realloc NullabilityOfRet: N diff --git a/test/APINotes/Inputs/roundtrip.apinotes b/test/APINotes/Inputs/roundtrip.apinotes index c34907ff67d..8509c79389b 100644 --- a/test/APINotes/Inputs/roundtrip.apinotes +++ b/test/APINotes/Inputs/roundtrip.apinotes @@ -2,6 +2,7 @@ Name: AppKit Availability: available AvailabilityMsg: '' +SwiftInferImportAsMember: true Classes: - Name: NSCell Availability: available From b3df0ce342b5562bd7b0bf31a23a2ea38efd1aca Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Fri, 1 Apr 2016 11:22:42 -0700 Subject: [PATCH 407/742] [APINotes] Apply ModuleOptions CompilerInstance will now check for ModuleOptions in the current module's APINotes, and apply them if present. --- include/clang/APINotes/APINotesManager.h | 4 ++++ lib/Frontend/CompilerInstance.cpp | 16 ++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/include/clang/APINotes/APINotesManager.h b/include/clang/APINotes/APINotesManager.h index 69752a9c77e..7f540b9c17d 100644 --- a/include/clang/APINotes/APINotesManager.h +++ b/include/clang/APINotes/APINotesManager.h @@ -111,6 +111,10 @@ class APINotesManager { /// Find the API notes reader that corresponds to the given source location. APINotesReader *findAPINotes(SourceLocation Loc); + + APINotesReader *getCurrentModuleReader() { + return CurrentModuleReader.get(); + } }; } // end namespace api_notes diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp index acb983af6a3..cdb9cf20a0b 100644 --- a/lib/Frontend/CompilerInstance.cpp +++ b/lib/Frontend/CompilerInstance.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "clang/Frontend/CompilerInstance.h" +#include "clang/APINotes/APINotesReader.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" @@ -533,10 +534,21 @@ void CompilerInstance::createSema(TranslationUnitKind TUKind, TUKind, CompletionConsumer)); // If we're building a module, notify the API notes manager. - if (!getLangOpts().CurrentModule.empty()) { + StringRef currentModuleName = getLangOpts().CurrentModule; + if (!currentModuleName.empty()) { (void)TheSema->APINotes.loadCurrentModuleAPINotes( - getLangOpts().CurrentModule, + currentModuleName, getAPINotesOpts().ModuleSearchPaths); + // Check for any attributes we should add to the module + if (auto curReader = TheSema->APINotes.getCurrentModuleReader()) { + auto currentModule = getPreprocessor().getCurrentModule(); + assert(currentModule && "how can we have a reader for it?"); + + // swift_infer_import_as_member + if (curReader->getModuleOptions().SwiftInferImportAsMember) { + currentModule->IsSwiftInferImportAsMember = true; + } + } } } From 04c0f2761a4333013d3a07509394612e445406b8 Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Fri, 1 Apr 2016 13:34:49 -0700 Subject: [PATCH 408/742] [APINotes] Incorporate Doug's feedback --- lib/APINotes/APINotesFormat.h | 2 +- lib/APINotes/APINotesReader.cpp | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/APINotes/APINotesFormat.h b/lib/APINotes/APINotesFormat.h index cae9ca962ae..0e8ca3368f6 100644 --- a/lib/APINotes/APINotesFormat.h +++ b/lib/APINotes/APINotesFormat.h @@ -35,7 +35,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 10; // enum constants +const uint16_t VERSION_MINOR = 11; // SwiftInferImportAsMember using IdentifierID = Fixnum<31>; using IdentifierIDField = BCVBR<16>; diff --git a/lib/APINotes/APINotesReader.cpp b/lib/APINotes/APINotesReader.cpp index 66a989920f8..d424b39e3aa 100644 --- a/lib/APINotes/APINotesReader.cpp +++ b/lib/APINotes/APINotesReader.cpp @@ -563,7 +563,7 @@ class APINotesReader::Implementation { std::string ModuleName; /// Various options and attributes for the module - ModuleOptions moduleOptions; + ModuleOptions ModuleOpts; using SerializedIdentifierTable = llvm::OnDiskIterableChainedHashTable; @@ -739,7 +739,8 @@ bool APINotesReader::Implementation::readControlBlock( break; case control_block::MODULE_OPTIONS: - moduleOptions = {(scratch.front() & 1) != 0}; + ModuleOpts.SwiftInferImportAsMember = (scratch.front() & 1) != 0; + break; default: // Unknown metadata record, possibly for use by a future version of the @@ -1447,7 +1448,7 @@ StringRef APINotesReader::getModuleName() const { } ModuleOptions APINotesReader::getModuleOptions() const { - return Impl.moduleOptions; + return Impl.ModuleOpts; } auto APINotesReader::lookupObjCClass(StringRef name) From 612703ed08f9546d2947eadef280a717bd067818 Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Sat, 2 Apr 2016 13:59:41 -0700 Subject: [PATCH 409/742] [Import as member] Default initialize new attribute to false --- lib/Basic/Module.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Basic/Module.cpp b/lib/Basic/Module.cpp index 9e5b5807516..10e4b6f4d73 100644 --- a/lib/Basic/Module.cpp +++ b/lib/Basic/Module.cpp @@ -31,7 +31,8 @@ Module::Module(StringRef Name, SourceLocation DefinitionLoc, Module *Parent, IsMissingRequirement(false), HasIncompatibleModuleFile(false), IsAvailable(true), IsFromModuleFile(false), IsFramework(IsFramework), IsExplicit(IsExplicit), IsSystem(false), IsExternC(false), - IsInferred(false), InferSubmodules(false), InferExplicitSubmodules(false), + IsInferred(false), IsSwiftInferImportAsMember(false), + InferSubmodules(false), InferExplicitSubmodules(false), InferExportWildcard(false), ConfigMacrosExhaustive(false), NameVisibility(Hidden) { if (Parent) { From a9fe51bf72f4b8accdfd20569f0a4961b57a597f Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Sat, 2 Apr 2016 13:59:41 -0700 Subject: [PATCH 410/742] [Import as member] Default initialize new attribute to false --- lib/Basic/Module.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Basic/Module.cpp b/lib/Basic/Module.cpp index 1fd12f15282..9c39ecb12ac 100644 --- a/lib/Basic/Module.cpp +++ b/lib/Basic/Module.cpp @@ -31,7 +31,8 @@ Module::Module(StringRef Name, SourceLocation DefinitionLoc, Module *Parent, IsMissingRequirement(false), HasIncompatibleModuleFile(false), IsAvailable(true), IsFromModuleFile(false), IsFramework(IsFramework), IsExplicit(IsExplicit), IsSystem(false), IsExternC(false), - IsInferred(false), InferSubmodules(false), InferExplicitSubmodules(false), + IsInferred(false), IsSwiftInferImportAsMember(false), + InferSubmodules(false), InferExplicitSubmodules(false), InferExportWildcard(false), ConfigMacrosExhaustive(false), NameVisibility(Hidden) { if (Parent) { From c98105c128a2afa4954dba922b07c5e86a8f0874 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Sun, 3 Apr 2016 03:36:22 +0000 Subject: [PATCH 411/742] Fix test case committed in r265197. The test was failing on some release build because the basic block names I was expecting weren't printed. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265257 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 241bba2e6902553bb403fec8ef05b7cebda894a6) --- test/CodeGenCXX/microsoft-abi-eh-cleanups.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/CodeGenCXX/microsoft-abi-eh-cleanups.cpp b/test/CodeGenCXX/microsoft-abi-eh-cleanups.cpp index 3afee1c1f12..298e70e7712 100644 --- a/test/CodeGenCXX/microsoft-abi-eh-cleanups.cpp +++ b/test/CodeGenCXX/microsoft-abi-eh-cleanups.cpp @@ -225,17 +225,17 @@ void f() { // WIN32-LIFETIME: invoke void @"\01?g@lifetime_marker@@YAXXZ"() // WIN32-LIFETIME-NEXT: to label %[[cont:[^ ]*]] unwind label %[[lpad0:[^ ]*]] // -// WIN32-LIFETIME: [[cont]]: +// WIN32-LIFETIME: [[cont]] // WIN32-LIFETIME: call x86_thiscallcc void @"\01??1C@lifetime_marker@@QAE@XZ"({{.*}}) // WIN32-LIFETIME: %[[bc1:.*]] = bitcast %"struct.lifetime_marker::C"* %[[c]] to i8* // WIN32-LIFETIME: call void @llvm.lifetime.end(i64 1, i8* %[[bc1]]) // -// WIN32-LIFETIME: [[lpad0]]: +// WIN32-LIFETIME: [[lpad0]] // WIN32-LIFETIME-NEXT: cleanuppad // WIN32-LIFETIME: call x86_thiscallcc void @"\01??1C@lifetime_marker@@QAE@XZ"({{.*}}) // WIN32-LIFETIME: cleanupret {{.*}} unwind label %[[lpad1:[^ ]*]] // -// WIN32-LIFETIME: [[lpad1]]: +// WIN32-LIFETIME: [[lpad1]] // WIN32-LIFETIME-NEXT: cleanuppad // WIN32-LIFETIME: %[[bc2:.*]] = bitcast %"struct.lifetime_marker::C"* %[[c]] to i8* // WIN32-LIFETIME: call void @llvm.lifetime.end(i64 1, i8* %[[bc2]]) From fcf9997e3fcc30508ccc884f29bfa622e170a6f2 Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Mon, 4 Apr 2016 11:48:49 -0700 Subject: [PATCH 412/742] [Import as member] ASTReader/Writer for swift_infer_import_as_member --- lib/Serialization/ASTReader.cpp | 2 ++ lib/Serialization/ASTWriter.cpp | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index b12c6b420be..ff1944ce41d 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -4471,6 +4471,7 @@ ASTReader::ReadSubmoduleBlock(ModuleFile &F, unsigned ClientLoadCapabilities) { bool IsExplicit = Record[Idx++]; bool IsSystem = Record[Idx++]; bool IsExternC = Record[Idx++]; + bool IsSwiftInferImportAsMember = Record[Idx++]; bool InferSubmodules = Record[Idx++]; bool InferExplicitSubmodules = Record[Idx++]; bool InferExportWildcard = Record[Idx++]; @@ -4515,6 +4516,7 @@ ASTReader::ReadSubmoduleBlock(ModuleFile &F, unsigned ClientLoadCapabilities) { CurrentModule->IsFromModuleFile = true; CurrentModule->IsSystem = IsSystem || CurrentModule->IsSystem; CurrentModule->IsExternC = IsExternC; + CurrentModule->IsSwiftInferImportAsMember = IsSwiftInferImportAsMember; CurrentModule->InferSubmodules = InferSubmodules; CurrentModule->InferExplicitSubmodules = InferExplicitSubmodules; CurrentModule->InferExportWildcard = InferExportWildcard; diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 0b28a7aa673..89064bdcbee 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -2398,6 +2398,7 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) { Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsExplicit Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsSystem Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsExternC + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsSwiftInferIAM... Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // InferSubmodules... Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // InferExplicit... Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // InferExportWild... @@ -2492,9 +2493,9 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) { { RecordData::value_type Record[] = { SUBMODULE_DEFINITION, ID, ParentID, Mod->IsFramework, Mod->IsExplicit, - Mod->IsSystem, Mod->IsExternC, Mod->InferSubmodules, - Mod->InferExplicitSubmodules, Mod->InferExportWildcard, - Mod->ConfigMacrosExhaustive}; + Mod->IsSystem, Mod->IsExternC, Mod->IsSwiftInferImportAsMember, + Mod->InferSubmodules, Mod->InferExplicitSubmodules, + Mod->InferExportWildcard, Mod->ConfigMacrosExhaustive}; Stream.EmitRecordWithBlob(DefinitionAbbrev, Record, Mod->Name); } From 780259e3964bb2064e5051c96abb5da5dce72884 Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Mon, 4 Apr 2016 11:48:49 -0700 Subject: [PATCH 413/742] [Import as member] ASTReader/Writer for swift_infer_import_as_member --- lib/Serialization/ASTReader.cpp | 2 ++ lib/Serialization/ASTWriter.cpp | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 962733f41d0..2316e570427 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -4471,6 +4471,7 @@ ASTReader::ReadSubmoduleBlock(ModuleFile &F, unsigned ClientLoadCapabilities) { bool IsExplicit = Record[Idx++]; bool IsSystem = Record[Idx++]; bool IsExternC = Record[Idx++]; + bool IsSwiftInferImportAsMember = Record[Idx++]; bool InferSubmodules = Record[Idx++]; bool InferExplicitSubmodules = Record[Idx++]; bool InferExportWildcard = Record[Idx++]; @@ -4515,6 +4516,7 @@ ASTReader::ReadSubmoduleBlock(ModuleFile &F, unsigned ClientLoadCapabilities) { CurrentModule->IsFromModuleFile = true; CurrentModule->IsSystem = IsSystem || CurrentModule->IsSystem; CurrentModule->IsExternC = IsExternC; + CurrentModule->IsSwiftInferImportAsMember = IsSwiftInferImportAsMember; CurrentModule->InferSubmodules = InferSubmodules; CurrentModule->InferExplicitSubmodules = InferExplicitSubmodules; CurrentModule->InferExportWildcard = InferExportWildcard; diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 0b28a7aa673..89064bdcbee 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -2398,6 +2398,7 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) { Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsExplicit Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsSystem Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsExternC + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsSwiftInferIAM... Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // InferSubmodules... Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // InferExplicit... Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // InferExportWild... @@ -2492,9 +2493,9 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) { { RecordData::value_type Record[] = { SUBMODULE_DEFINITION, ID, ParentID, Mod->IsFramework, Mod->IsExplicit, - Mod->IsSystem, Mod->IsExternC, Mod->InferSubmodules, - Mod->InferExplicitSubmodules, Mod->InferExportWildcard, - Mod->ConfigMacrosExhaustive}; + Mod->IsSystem, Mod->IsExternC, Mod->IsSwiftInferImportAsMember, + Mod->InferSubmodules, Mod->InferExplicitSubmodules, + Mod->InferExportWildcard, Mod->ConfigMacrosExhaustive}; Stream.EmitRecordWithBlob(DefinitionAbbrev, Record, Mod->Name); } From e0f09f65fc0005b7a48cf1f62df46b799286bcb7 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 1 Mar 2016 00:18:05 +0000 Subject: [PATCH 414/742] Infrastructure improvements to Clang attribute TableGen. This should make it easier to add new Attr subclasses. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262275 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Attr.h | 8 +- include/clang/Basic/AttrKinds.h | 6 +- lib/AST/ASTDumper.cpp | 2 - utils/TableGen/ClangAttrEmitter.cpp | 280 ++++++++++++++++++++-------- 4 files changed, 205 insertions(+), 91 deletions(-) diff --git a/include/clang/AST/Attr.h b/include/clang/AST/Attr.h index 8b80e9f6396..698909f0335 100644 --- a/include/clang/AST/Attr.h +++ b/include/clang/AST/Attr.h @@ -129,7 +129,8 @@ class InheritableAttr : public Attr { // Implement isa/cast/dyncast/etc. static bool classof(const Attr *A) { - return A->getKind() <= attr::LAST_INHERITABLE; + return A->getKind() >= attr::FirstInheritableAttr && + A->getKind() <= attr::LastInheritableAttr; } }; @@ -143,9 +144,8 @@ class InheritableParamAttr : public InheritableAttr { public: // Implement isa/cast/dyncast/etc. static bool classof(const Attr *A) { - // Relies on relative order of enum emission with respect to MS inheritance - // attrs. - return A->getKind() <= attr::LAST_INHERITABLE_PARAM; + return A->getKind() >= attr::FirstInheritableParamAttr && + A->getKind() <= attr::LastInheritableParamAttr; } }; diff --git a/include/clang/Basic/AttrKinds.h b/include/clang/Basic/AttrKinds.h index f0b0a6445d4..8f7394f59d4 100644 --- a/include/clang/Basic/AttrKinds.h +++ b/include/clang/Basic/AttrKinds.h @@ -22,10 +22,10 @@ namespace attr { // \brief A list of all the recognized kinds of attributes. enum Kind { #define ATTR(X) X, -#define LAST_INHERITABLE_ATTR(X) X, LAST_INHERITABLE = X, -#define LAST_INHERITABLE_PARAM_ATTR(X) X, LAST_INHERITABLE_PARAM = X, +#define ATTR_RANGE(CLASS, FIRST_NAME, LAST_NAME) \ + First##CLASS = FIRST_NAME, \ + Last##CLASS = LAST_NAME, #include "clang/Basic/AttrList.inc" - NUM_ATTRS }; } // end namespace attr diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 9b7944271af..76401f56869 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -819,8 +819,6 @@ void ASTDumper::dumpAttr(const Attr *A) { switch (A->getKind()) { #define ATTR(X) case attr::X: OS << #X; break; #include "clang/Basic/AttrList.inc" - default: - llvm_unreachable("unexpected attribute kind"); } OS << "Attr"; } diff --git a/utils/TableGen/ClangAttrEmitter.cpp b/utils/TableGen/ClangAttrEmitter.cpp index c0574449acc..e2cabe67645 100644 --- a/utils/TableGen/ClangAttrEmitter.cpp +++ b/utils/TableGen/ClangAttrEmitter.cpp @@ -1705,8 +1705,6 @@ void EmitClangAttrImpl(RecordKeeper &Records, raw_ostream &OS) { OS << " return cast<" << R.getName() << "Attr>(this)->" << Method << ";\n"; } - OS << " case attr::NUM_ATTRS:\n"; - OS << " break;\n"; OS << " }\n"; OS << " llvm_unreachable(\"Unexpected attribute kind!\");\n"; OS << "}\n\n"; @@ -1725,20 +1723,10 @@ void EmitClangAttrImpl(RecordKeeper &Records, raw_ostream &OS) { } // end namespace clang -static void EmitAttrList(raw_ostream &OS, StringRef Class, +static void emitAttrList(raw_ostream &OS, StringRef Class, const std::vector &AttrList) { - auto i = AttrList.cbegin(), e = AttrList.cend(); - - if (i != e) { - // Move the end iterator back to emit the last attribute. - for(--e; i != e; ++i) { - if (!(*i)->getValueAsBit("ASTNode")) - continue; - - OS << Class << "(" << (*i)->getName() << ")\n"; - } - - OS << "LAST_" << Class << "(" << (*i)->getName() << ")\n\n"; + for (auto Cur : AttrList) { + OS << Class << "(" << Cur->getName() << ")\n"; } } @@ -1751,71 +1739,216 @@ static bool AttrHasPragmaSpelling(const Record *R) { }) != Spellings.end(); } -namespace clang { -// Emits the enumeration list for attributes. -void EmitClangAttrList(RecordKeeper &Records, raw_ostream &OS) { - emitSourceFileHeader("List of all attributes that Clang recognizes", OS); +namespace { + struct AttrClassDescriptor { + const char * const MacroName; + const char * const TableGenName; + }; +} - OS << "#ifndef LAST_ATTR\n"; - OS << "#define LAST_ATTR(NAME) ATTR(NAME)\n"; - OS << "#endif\n\n"; +static const AttrClassDescriptor AttrClassDescriptors[] = { + { "ATTR", "Attr" }, + { "INHERITABLE_ATTR", "InheritableAttr" }, + { "INHERITABLE_PARAM_ATTR", "InheritableParamAttr" } +}; - OS << "#ifndef INHERITABLE_ATTR\n"; - OS << "#define INHERITABLE_ATTR(NAME) ATTR(NAME)\n"; - OS << "#endif\n\n"; +static void emitDefaultDefine(raw_ostream &OS, StringRef name, + const char *superName) { + OS << "#ifndef " << name << "\n"; + OS << "#define " << name << "(NAME) "; + if (superName) OS << superName << "(NAME)"; + OS << "\n#endif\n\n"; +} - OS << "#ifndef LAST_INHERITABLE_ATTR\n"; - OS << "#define LAST_INHERITABLE_ATTR(NAME) INHERITABLE_ATTR(NAME)\n"; - OS << "#endif\n\n"; +namespace { + /// A class of attributes. + struct AttrClass { + const AttrClassDescriptor &Descriptor; + Record *TheRecord; + AttrClass *SuperClass = nullptr; + std::vector SubClasses; + std::vector Attrs; + + AttrClass(const AttrClassDescriptor &Descriptor, Record *R) + : Descriptor(Descriptor), TheRecord(R) {} + + void emitDefaultDefines(raw_ostream &OS) const { + // Default the macro unless this is a root class (i.e. Attr). + if (SuperClass) { + emitDefaultDefine(OS, Descriptor.MacroName, + SuperClass->Descriptor.MacroName); + } + } - OS << "#ifndef INHERITABLE_PARAM_ATTR\n"; - OS << "#define INHERITABLE_PARAM_ATTR(NAME) ATTR(NAME)\n"; - OS << "#endif\n\n"; + void emitUndefs(raw_ostream &OS) const { + OS << "#undef " << Descriptor.MacroName << "\n"; + } - OS << "#ifndef LAST_INHERITABLE_PARAM_ATTR\n"; - OS << "#define LAST_INHERITABLE_PARAM_ATTR(NAME)" - " INHERITABLE_PARAM_ATTR(NAME)\n"; - OS << "#endif\n\n"; + void emitAttrList(raw_ostream &OS) const { + for (auto SubClass : SubClasses) { + SubClass->emitAttrList(OS); + } - OS << "#ifndef PRAGMA_SPELLING_ATTR\n"; - OS << "#define PRAGMA_SPELLING_ATTR(NAME)\n"; - OS << "#endif\n\n"; + ::emitAttrList(OS, Descriptor.MacroName, Attrs); + } - OS << "#ifndef LAST_PRAGMA_SPELLING_ATTR\n"; - OS << "#define LAST_PRAGMA_SPELLING_ATTR(NAME) PRAGMA_SPELLING_ATTR(NAME)\n"; - OS << "#endif\n\n"; + void classifyAttrOnRoot(Record *Attr) { + bool result = classifyAttr(Attr); + assert(result && "failed to classify on root"); (void) result; + } - Record *InhClass = Records.getClass("InheritableAttr"); - Record *InhParamClass = Records.getClass("InheritableParamAttr"); - std::vector Attrs = Records.getAllDerivedDefinitions("Attr"), - NonInhAttrs, InhAttrs, InhParamAttrs, PragmaAttrs; + void emitAttrRange(raw_ostream &OS) const { + OS << "ATTR_RANGE(" << Descriptor.TableGenName + << ", " << getFirstAttr()->getName() + << ", " << getLastAttr()->getName() << ")\n"; + } + + private: + bool classifyAttr(Record *Attr) { + // Check all the subclasses. + for (auto SubClass : SubClasses) { + if (SubClass->classifyAttr(Attr)) + return true; + } + + // It's not more specific than this class, but it might still belong here. + if (Attr->isSubClassOf(TheRecord)) { + Attrs.push_back(Attr); + return true; + } + + return false; + } + + Record *getFirstAttr() const { + if (!SubClasses.empty()) + return SubClasses.front()->getFirstAttr(); + return Attrs.front(); + } + + Record *getLastAttr() const { + if (!Attrs.empty()) + return Attrs.back(); + return SubClasses.back()->getLastAttr(); + } + }; + + /// The entire hierarchy of attribute classes. + class AttrClassHierarchy { + std::vector> Classes; + public: + AttrClassHierarchy(RecordKeeper &Records) { + // Find records for all the classes. + for (auto &Descriptor : AttrClassDescriptors) { + Record *ClassRecord = Records.getClass(Descriptor.TableGenName); + AttrClass *Class = new AttrClass(Descriptor, ClassRecord); + Classes.emplace_back(Class); + } + + // Link up the hierarchy. + for (auto &Class : Classes) { + if (AttrClass *SuperClass = findSuperClass(Class->TheRecord)) { + Class->SuperClass = SuperClass; + SuperClass->SubClasses.push_back(Class.get()); + } + } + +#ifndef NDEBUG + for (auto i = Classes.begin(), e = Classes.end(); i != e; ++i) { + assert((i == Classes.begin()) == ((*i)->SuperClass == nullptr) && + "only the first class should be a root class!"); + } +#endif + } + + void emitDefaultDefines(raw_ostream &OS) const { + for (auto &Class : Classes) { + Class->emitDefaultDefines(OS); + } + } + + void emitUndefs(raw_ostream &OS) const { + for (auto &Class : Classes) { + Class->emitUndefs(OS); + } + } + + void emitAttrLists(raw_ostream &OS) const { + // Just start from the root class. + Classes[0]->emitAttrList(OS); + } + + void emitAttrRanges(raw_ostream &OS) const { + for (auto &Class : Classes) + Class->emitAttrRange(OS); + } + + void classifyAttr(Record *Attr) { + // Add the attribute to the root class. + Classes[0]->classifyAttrOnRoot(Attr); + } + + private: + AttrClass *findClassByRecord(Record *R) const { + for (auto &Class : Classes) { + if (Class->TheRecord == R) + return Class.get(); + } + return nullptr; + } + + AttrClass *findSuperClass(Record *R) const { + // TableGen flattens the superclass list, so we just need to walk it + // in reverse. + auto SuperClasses = R->getSuperClasses(); + for (signed i = 0, e = SuperClasses.size(); i != e; ++i) { + auto SuperClass = findClassByRecord(SuperClasses[e - i - 1].first); + if (SuperClass) return SuperClass; + } + return nullptr; + } + }; +} + +namespace clang { +// Emits the enumeration list for attributes. +void EmitClangAttrList(RecordKeeper &Records, raw_ostream &OS) { + emitSourceFileHeader("List of all attributes that Clang recognizes", OS); + + AttrClassHierarchy Hierarchy(Records); + + // Add defaulting macro definitions. + Hierarchy.emitDefaultDefines(OS); + emitDefaultDefine(OS, "PRAGMA_SPELLING_ATTR", nullptr); + + std::vector Attrs = Records.getAllDerivedDefinitions("Attr"); + std::vector PragmaAttrs; for (auto *Attr : Attrs) { if (!Attr->getValueAsBit("ASTNode")) continue; + // Add the attribute to the ad-hoc groups. if (AttrHasPragmaSpelling(Attr)) PragmaAttrs.push_back(Attr); - if (Attr->isSubClassOf(InhParamClass)) - InhParamAttrs.push_back(Attr); - else if (Attr->isSubClassOf(InhClass)) - InhAttrs.push_back(Attr); - else - NonInhAttrs.push_back(Attr); + // Place it in the hierarchy. + Hierarchy.classifyAttr(Attr); } - EmitAttrList(OS, "PRAGMA_SPELLING_ATTR", PragmaAttrs); - EmitAttrList(OS, "INHERITABLE_PARAM_ATTR", InhParamAttrs); - EmitAttrList(OS, "INHERITABLE_ATTR", InhAttrs); - EmitAttrList(OS, "ATTR", NonInhAttrs); + // Emit the main attribute list. + Hierarchy.emitAttrLists(OS); + + // Emit the ad hoc groups. + emitAttrList(OS, "PRAGMA_SPELLING_ATTR", PragmaAttrs); - OS << "#undef LAST_ATTR\n"; - OS << "#undef INHERITABLE_ATTR\n"; - OS << "#undef LAST_INHERITABLE_ATTR\n"; - OS << "#undef LAST_INHERITABLE_PARAM_ATTR\n"; - OS << "#undef LAST_PRAGMA_ATTR\n"; + // Emit the attribute ranges. + OS << "#ifdef ATTR_RANGE\n"; + Hierarchy.emitAttrRanges(OS); + OS << "#undef ATTR_RANGE\n"; + OS << "#endif\n"; + + Hierarchy.emitUndefs(OS); OS << "#undef PRAGMA_SPELLING_ATTR\n"; - OS << "#undef ATTR\n"; } // Emits the code to read an attribute from a precompiled header. @@ -1828,8 +1961,6 @@ void EmitClangAttrPCHRead(RecordKeeper &Records, raw_ostream &OS) { std::vector> Args; OS << " switch (Kind) {\n"; - OS << " default:\n"; - OS << " llvm_unreachable(\"Unknown attribute!\");\n"; for (const auto *Attr : Attrs) { const Record &R = *Attr; if (!R.getValueAsBit("ASTNode")) @@ -1869,9 +2000,6 @@ void EmitClangAttrPCHWrite(RecordKeeper &Records, raw_ostream &OS) { std::vector Attrs = Records.getAllDerivedDefinitions("Attr"), Args; OS << " switch (A->getKind()) {\n"; - OS << " default:\n"; - OS << " llvm_unreachable(\"Unknown attribute kind!\");\n"; - OS << " break;\n"; for (const auto *Attr : Attrs) { const Record &R = *Attr; if (!R.getValueAsBit("ASTNode")) @@ -2062,11 +2190,7 @@ void EmitClangAttrSpellingListIndex(RecordKeeper &Records, raw_ostream &OS) { emitSourceFileHeader("Code to translate different attribute spellings " "into internal identifiers", OS); - OS << - " switch (AttrKind) {\n" - " default:\n" - " llvm_unreachable(\"Unknown attribute kind!\");\n" - " break;\n"; + OS << " switch (AttrKind) {\n"; ParsedAttrMap Attrs = getParsedAttrList(Records); for (const auto &I : Attrs) { @@ -2146,9 +2270,7 @@ void EmitClangAttrASTVisitor(RecordKeeper &Records, raw_ostream &OS) { << " if (!A)\n" << " return true;\n" << "\n" - << " switch (A->getKind()) {\n" - << " default:\n" - << " return true;\n"; + << " switch (A->getKind()) {\n"; for (const auto *Attr : Attrs) { const Record &R = *Attr; @@ -2175,9 +2297,7 @@ void EmitClangAttrTemplateInstantiate(RecordKeeper &Records, raw_ostream &OS) { << "Attr *instantiateTemplateAttribute(const Attr *At, ASTContext &C, " << "Sema &S,\n" << " const MultiLevelTemplateArgumentList &TemplateArgs) {\n" - << " switch (At->getKind()) {\n" - << " default:\n" - << " break;\n"; + << " switch (At->getKind()) {\n"; for (const auto *Attr : Attrs) { const Record &R = *Attr; @@ -2775,11 +2895,7 @@ void EmitClangAttrParsedAttrKinds(RecordKeeper &Records, raw_ostream &OS) { void EmitClangAttrDump(RecordKeeper &Records, raw_ostream &OS) { emitSourceFileHeader("Attribute dumper", OS); - OS << - " switch (A->getKind()) {\n" - " default:\n" - " llvm_unreachable(\"Unknown attribute kind!\");\n" - " break;\n"; + OS << " switch (A->getKind()) {\n"; std::vector Attrs = Records.getAllDerivedDefinitions("Attr"), Args; for (const auto *Attr : Attrs) { const Record &R = *Attr; From a3449a229a99cdab71eda5fec328749b3cce5c81 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 1 Mar 2016 00:49:02 +0000 Subject: [PATCH 415/742] Generalize the consumed-parameter array on FunctionProtoType to allow arbitrary data to be associated with a parameter. Also, fix a bug where we apparently haven't been serializing this information for the last N years. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262278 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/ASTContext.h | 2 +- include/clang/AST/Type.h | 110 +++++++++++++++++++++++++------- lib/AST/ASTContext.cpp | 75 ++++++++++++---------- lib/AST/Type.cpp | 13 ++-- lib/Sema/SemaOverload.cpp | 5 +- lib/Sema/SemaType.cpp | 33 ++++++---- lib/Serialization/ASTReader.cpp | 11 ++++ lib/Serialization/ASTWriter.cpp | 8 ++- 8 files changed, 178 insertions(+), 79 deletions(-) diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index 438e676a979..fb5a64f5518 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -2260,7 +2260,7 @@ class ASTContext : public RefCountedBase { QualType mergeObjCGCQualifiers(QualType, QualType); - bool FunctionTypesMatchOnNSConsumedAttrs( + bool doFunctionTypesMatchOnExtParameterInfos( const FunctionProtoType *FromFunctionType, const FunctionProtoType *ToFunctionType); diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 2929037a87c..7b04058d7f9 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -3040,6 +3040,44 @@ class FunctionNoProtoType : public FunctionType, public llvm::FoldingSetNode { /// type. class FunctionProtoType : public FunctionType, public llvm::FoldingSetNode { public: + class ExtParameterInfo { + enum { + IsConsumed = 0x01, + }; + unsigned char Data; + public: + ExtParameterInfo() : Data(0) {} + + /// Is this parameter considered "consumed" by Objective-C ARC? + /// Consumed parameters must have retainable object type. + bool isConsumed() const { + return (Data & IsConsumed); + } + ExtParameterInfo withIsConsumed(bool consumed) const { + ExtParameterInfo copy = *this; + if (consumed) { + copy.Data |= IsConsumed; + } else { + copy.Data &= ~IsConsumed; + } + return copy; + } + + unsigned char getOpaqueValue() const { return Data; } + static ExtParameterInfo getFromOpaqueValue(unsigned char data) { + ExtParameterInfo result; + result.Data = data; + return result; + } + + friend bool operator==(ExtParameterInfo lhs, ExtParameterInfo rhs) { + return lhs.Data == rhs.Data; + } + friend bool operator!=(ExtParameterInfo lhs, ExtParameterInfo rhs) { + return lhs.Data != rhs.Data; + } + }; + struct ExceptionSpecInfo { ExceptionSpecInfo() : Type(EST_None), NoexceptExpr(nullptr), @@ -3067,11 +3105,11 @@ class FunctionProtoType : public FunctionType, public llvm::FoldingSetNode { struct ExtProtoInfo { ExtProtoInfo() : Variadic(false), HasTrailingReturn(false), TypeQuals(0), - RefQualifier(RQ_None), ConsumedParameters(nullptr) {} + RefQualifier(RQ_None), ExtParameterInfos(nullptr) {} ExtProtoInfo(CallingConv CC) : ExtInfo(CC), Variadic(false), HasTrailingReturn(false), TypeQuals(0), - RefQualifier(RQ_None), ConsumedParameters(nullptr) {} + RefQualifier(RQ_None), ExtParameterInfos(nullptr) {} ExtProtoInfo withExceptionSpec(const ExceptionSpecInfo &O) { ExtProtoInfo Result(*this); @@ -3085,7 +3123,7 @@ class FunctionProtoType : public FunctionType, public llvm::FoldingSetNode { unsigned char TypeQuals; RefQualifierKind RefQualifier; ExceptionSpecInfo ExceptionSpec; - const bool *ConsumedParameters; + const ExtParameterInfo *ExtParameterInfos; }; private: @@ -3112,8 +3150,8 @@ class FunctionProtoType : public FunctionType, public llvm::FoldingSetNode { /// The type of exception specification this function has. unsigned ExceptionSpecType : 4; - /// Whether this function has any consumed parameters. - unsigned HasAnyConsumedParams : 1; + /// Whether this function has extended parameter information. + unsigned HasExtParameterInfos : 1; /// Whether the function is variadic. unsigned Variadic : 1; @@ -3135,25 +3173,36 @@ class FunctionProtoType : public FunctionType, public llvm::FoldingSetNode { // instantiate this function type's exception specification, and the function // from which it should be instantiated. - // ConsumedParameters - A variable size array, following Exceptions - // and of length NumParams, holding flags indicating which parameters - // are consumed. This only appears if HasAnyConsumedParams is true. + // ExtParameterInfos - A variable size array, following the exception + // specification and of length NumParams, holding an ExtParameterInfo + // for each of the parameters. This only appears if HasExtParameterInfos + // is true. friend class ASTContext; // ASTContext creates these. - const bool *getConsumedParamsBuffer() const { - assert(hasAnyConsumedParams()); + const ExtParameterInfo *getExtParameterInfosBuffer() const { + assert(hasExtParameterInfos()); - // Find the end of the exceptions. - Expr *const *eh_end = reinterpret_cast(exception_end()); - if (getExceptionSpecType() == EST_ComputedNoexcept) - eh_end += 1; // NoexceptExpr - // The memory layout of these types isn't handled here, so - // hopefully this is never called for them? - assert(getExceptionSpecType() != EST_Uninstantiated && - getExceptionSpecType() != EST_Unevaluated); + // Find the end of the exception specification. + const char *ptr = reinterpret_cast(exception_begin()); + ptr += getExceptionSpecSize(); - return reinterpret_cast(eh_end); + return reinterpret_cast(ptr); + } + + size_t getExceptionSpecSize() const { + switch (getExceptionSpecType()) { + case EST_None: return 0; + case EST_DynamicNone: return 0; + case EST_MSAny: return 0; + case EST_BasicNoexcept: return 0; + case EST_Unparsed: return 0; + case EST_Dynamic: return getNumExceptions() * sizeof(QualType); + case EST_ComputedNoexcept: return sizeof(Expr*); + case EST_Uninstantiated: return 2 * sizeof(FunctionDecl*); + case EST_Unevaluated: return sizeof(FunctionDecl*); + } + llvm_unreachable("bad exception specification kind"); } public: @@ -3184,8 +3233,8 @@ class FunctionProtoType : public FunctionType, public llvm::FoldingSetNode { } else if (EPI.ExceptionSpec.Type == EST_Unevaluated) { EPI.ExceptionSpec.SourceDecl = getExceptionSpecDecl(); } - if (hasAnyConsumedParams()) - EPI.ConsumedParameters = getConsumedParamsBuffer(); + if (hasExtParameterInfos()) + EPI.ExtParameterInfos = getExtParameterInfosBuffer(); return EPI; } @@ -3300,11 +3349,24 @@ class FunctionProtoType : public FunctionType, public llvm::FoldingSetNode { return exception_begin() + NumExceptions; } - bool hasAnyConsumedParams() const { return HasAnyConsumedParams; } + bool hasExtParameterInfos() const { return HasExtParameterInfos; } + ArrayRef getExtParameterInfos() const { + assert(hasExtParameterInfos()); + return ArrayRef(getExtParameterInfosBuffer(), + getNumParams()); + } + + ExtParameterInfo getExtParameterInfo(unsigned I) const { + assert(I < getNumParams() && "parameter index out of range"); + if (hasExtParameterInfos()) + return getExtParameterInfosBuffer()[I]; + return ExtParameterInfo(); + } + bool isParamConsumed(unsigned I) const { assert(I < getNumParams() && "parameter index out of range"); - if (hasAnyConsumedParams()) - return getConsumedParamsBuffer()[I]; + if (hasExtParameterInfos()) + return getExtParameterInfosBuffer()[I].isConsumed(); return false; } diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 06fb46e2f7f..e631ff88889 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -2992,13 +2992,18 @@ ASTContext::getDependentSizedExtVectorType(QualType vecType, return QualType(New, 0); } +/// \brief Determine whether \p T is canonical as the result type of a function. +static bool isCanonicalResultType(QualType T) { + return T.isCanonical() && + (T.getObjCLifetime() == Qualifiers::OCL_None || + T.getObjCLifetime() == Qualifiers::OCL_ExplicitNone); +} + /// getFunctionNoProtoType - Return a K&R style C function type like 'int()'. /// QualType ASTContext::getFunctionNoProtoType(QualType ResultTy, const FunctionType::ExtInfo &Info) const { - const CallingConv CallConv = Info.getCC(); - // Unique functions, to guarantee there is only one function of a particular // structure. llvm::FoldingSetNodeID ID; @@ -3010,8 +3015,9 @@ ASTContext::getFunctionNoProtoType(QualType ResultTy, return QualType(FT, 0); QualType Canonical; - if (!ResultTy.isCanonical()) { - Canonical = getFunctionNoProtoType(getCanonicalType(ResultTy), Info); + if (!isCanonicalResultType(ResultTy)) { + Canonical = + getFunctionNoProtoType(getCanonicalFunctionResultType(ResultTy), Info); // Get the new insert position for the node we care about. FunctionNoProtoType *NewIP = @@ -3019,21 +3025,13 @@ ASTContext::getFunctionNoProtoType(QualType ResultTy, assert(!NewIP && "Shouldn't be in the map!"); (void)NewIP; } - FunctionProtoType::ExtInfo newInfo = Info.withCallingConv(CallConv); FunctionNoProtoType *New = new (*this, TypeAlignment) - FunctionNoProtoType(ResultTy, Canonical, newInfo); + FunctionNoProtoType(ResultTy, Canonical, Info); Types.push_back(New); FunctionNoProtoTypes.InsertNode(New, InsertPos); return QualType(New, 0); } -/// \brief Determine whether \p T is canonical as the result type of a function. -static bool isCanonicalResultType(QualType T) { - return T.isCanonical() && - (T.getObjCLifetime() == Qualifiers::OCL_None || - T.getObjCLifetime() == Qualifiers::OCL_ExplicitNone); -} - CanQualType ASTContext::getCanonicalFunctionResultType(QualType ResultType) const { CanQualType CanResultType = getCanonicalType(ResultType); @@ -3100,12 +3098,13 @@ ASTContext::getFunctionType(QualType ResultTy, ArrayRef ArgArray, // them for three variable size arrays at the end: // - parameter types // - exception types - // - consumed-arguments flags + // - extended parameter information // Instead of the exception types, there could be a noexcept // expression, or information used to resolve the exception // specification. size_t Size = sizeof(FunctionProtoType) + NumArgs * sizeof(QualType); + if (EPI.ExceptionSpec.Type == EST_Dynamic) { Size += EPI.ExceptionSpec.Exceptions.size() * sizeof(QualType); } else if (EPI.ExceptionSpec.Type == EST_ComputedNoexcept) { @@ -3115,8 +3114,16 @@ ASTContext::getFunctionType(QualType ResultTy, ArrayRef ArgArray, } else if (EPI.ExceptionSpec.Type == EST_Unevaluated) { Size += sizeof(FunctionDecl*); } - if (EPI.ConsumedParameters) - Size += NumArgs * sizeof(bool); + + // Put the ExtParameterInfos last. If all were equal, it would make + // more sense to put these before the exception specification, because + // it's much easier to skip past them compared to the elaborate switch + // required to skip the exception specification. However, all is not + // equal; ExtParameterInfos are used to model very uncommon features, + // and it's better not to burden the more common paths. + if (EPI.ExtParameterInfos) { + Size += NumArgs * sizeof(FunctionProtoType::ExtParameterInfo); + } FunctionProtoType *FTP = (FunctionProtoType*) Allocate(Size, TypeAlignment); FunctionProtoType::ExtProtoInfo newEPI = EPI; @@ -7483,8 +7490,7 @@ QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs, if (lproto->getTypeQuals() != rproto->getTypeQuals()) return QualType(); - if (LangOpts.ObjCAutoRefCount && - !FunctionTypesMatchOnNSConsumedAttrs(rproto, lproto)) + if (!doFunctionTypesMatchOnExtParameterInfos(rproto, lproto)) return QualType(); // Check parameter type compatibility @@ -7872,21 +7878,26 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, llvm_unreachable("Invalid Type::Class!"); } -bool ASTContext::FunctionTypesMatchOnNSConsumedAttrs( - const FunctionProtoType *FromFunctionType, - const FunctionProtoType *ToFunctionType) { - if (FromFunctionType->hasAnyConsumedParams() != - ToFunctionType->hasAnyConsumedParams()) +bool ASTContext::doFunctionTypesMatchOnExtParameterInfos( + const FunctionProtoType *firstFnType, + const FunctionProtoType *secondFnType) { + // Fast path: if the first type doesn't have ext parameter infos, + // we match if and only if they second type also doesn't have them. + if (!firstFnType->hasExtParameterInfos()) + return !secondFnType->hasExtParameterInfos(); + + // Otherwise, we can only match if the second type has them. + if (!secondFnType->hasExtParameterInfos()) return false; - FunctionProtoType::ExtProtoInfo FromEPI = - FromFunctionType->getExtProtoInfo(); - FunctionProtoType::ExtProtoInfo ToEPI = - ToFunctionType->getExtProtoInfo(); - if (FromEPI.ConsumedParameters && ToEPI.ConsumedParameters) - for (unsigned i = 0, n = FromFunctionType->getNumParams(); i != n; ++i) { - if (FromEPI.ConsumedParameters[i] != ToEPI.ConsumedParameters[i]) - return false; - } + + auto firstEPI = firstFnType->getExtParameterInfos(); + auto secondEPI = secondFnType->getExtParameterInfos(); + assert(firstEPI.size() == secondEPI.size()); + + for (size_t i = 0, n = firstEPI.size(); i != n; ++i) { + if (firstEPI[i] != secondEPI[i]) + return false; + } return true; } diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 721b8b21a73..a1b457b51a2 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -2673,7 +2673,7 @@ FunctionProtoType::FunctionProtoType(QualType result, ArrayRef params, NumParams(params.size()), NumExceptions(epi.ExceptionSpec.Exceptions.size()), ExceptionSpecType(epi.ExceptionSpec.Type), - HasAnyConsumedParams(epi.ConsumedParameters != nullptr), + HasExtParameterInfos(epi.ExtParameterInfos != nullptr), Variadic(epi.Variadic), HasTrailingReturn(epi.HasTrailingReturn) { assert(NumParams == params.size() && "function has too many parameters"); @@ -2739,10 +2739,11 @@ FunctionProtoType::FunctionProtoType(QualType result, ArrayRef params, slot[0] = epi.ExceptionSpec.SourceDecl; } - if (epi.ConsumedParameters) { - bool *consumedParams = const_cast(getConsumedParamsBuffer()); + if (epi.ExtParameterInfos) { + ExtParameterInfo *extParamInfos = + const_cast(getExtParameterInfosBuffer()); for (unsigned i = 0; i != NumParams; ++i) - consumedParams[i] = epi.ConsumedParameters[i]; + extParamInfos[i] = epi.ExtParameterInfos[i]; } } @@ -2862,9 +2863,9 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result, epi.ExceptionSpec.Type == EST_Unevaluated) { ID.AddPointer(epi.ExceptionSpec.SourceDecl->getCanonicalDecl()); } - if (epi.ConsumedParameters) { + if (epi.ExtParameterInfos) { for (unsigned i = 0; i != NumParams; ++i) - ID.AddBoolean(epi.ConsumedParameters[i]); + ID.AddInteger(epi.ExtParameterInfos[i].getOpaqueValue()); } epi.ExtInfo.Profile(ID); ID.AddBoolean(epi.HasTrailingReturn); diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index c9293fa4c82..55773f1c60f 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -2538,9 +2538,8 @@ bool Sema::IsBlockPointerConversion(QualType FromType, QualType ToType, // Argument types are too different. Abort. return false; } - if (LangOpts.ObjCAutoRefCount && - !Context.FunctionTypesMatchOnNSConsumedAttrs(FromFunctionType, - ToFunctionType)) + if (!Context.doFunctionTypesMatchOnExtParameterInfos(FromFunctionType, + ToFunctionType)) return false; ConvertedType = ToType; diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 4e37fc9f0cb..e4b98feeed8 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -3973,9 +3973,9 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, SmallVector ParamTys; ParamTys.reserve(FTI.NumParams); - SmallVector ConsumedParameters; - ConsumedParameters.reserve(FTI.NumParams); - bool HasAnyConsumedParameters = false; + SmallVector + ExtParameterInfos(FTI.NumParams); + bool HasAnyInterestingExtParameterInfos = false; for (unsigned i = 0, e = FTI.NumParams; i != e; ++i) { ParmVarDecl *Param = cast(FTI.Params[i].Param); @@ -4033,17 +4033,16 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, } } - if (LangOpts.ObjCAutoRefCount) { - bool Consumed = Param->hasAttr(); - ConsumedParameters.push_back(Consumed); - HasAnyConsumedParameters |= Consumed; + if (LangOpts.ObjCAutoRefCount && Param->hasAttr()) { + ExtParameterInfos[i] = ExtParameterInfos[i].withIsConsumed(true); + HasAnyInterestingExtParameterInfos = true; } ParamTys.push_back(ParamTy); } - if (HasAnyConsumedParameters) - EPI.ConsumedParameters = ConsumedParameters.data(); + if (HasAnyInterestingExtParameterInfos) + EPI.ExtParameterInfos = ExtParameterInfos.data(); SmallVector Exceptions; SmallVector DynamicExceptions; @@ -5952,18 +5951,28 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state, } } - // Diagnose use of callee-cleanup calling convention on variadic functions. + // Diagnose use of variadic functions with calling conventions that + // don't support them (e.g. because they're callee-cleanup). + // We delay warning about this on unprototyped function declarations + // until after redeclaration checking, just in case we pick up a + // prototype that way. And apparently we also "delay" warning about + // unprototyped function types in general, despite not necessarily having + // much ability to diagnose it later. if (!supportsVariadicCall(CC)) { const FunctionProtoType *FnP = dyn_cast(fn); if (FnP && FnP->isVariadic()) { unsigned DiagID = diag::err_cconv_varargs; + // stdcall and fastcall are ignored with a warning for GCC and MS // compatibility. - if (CC == CC_X86StdCall || CC == CC_X86FastCall) + bool IsInvalid = true; + if (CC == CC_X86StdCall || CC == CC_X86FastCall) { DiagID = diag::warn_cconv_varargs; + IsInvalid = false; + } S.Diag(attr.getLoc(), DiagID) << FunctionType::getNameForCallConv(CC); - attr.setInvalid(); + if (IsInvalid) attr.setInvalid(); return true; } } diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index ff1944ce41d..9afa782684c 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -5379,6 +5379,17 @@ QualType ASTReader::readTypeRecord(unsigned Index) { for (unsigned I = 0; I != NumParams; ++I) ParamTypes.push_back(readType(*Loc.F, Record, Idx)); + SmallVector ExtParameterInfos; + if (Idx != Record.size()) { + for (unsigned I = 0; I != NumParams; ++I) + ExtParameterInfos.push_back( + FunctionProtoType::ExtParameterInfo + ::getFromOpaqueValue(Record[Idx++])); + EPI.ExtParameterInfos = ExtParameterInfos.data(); + } + + assert(Idx == Record.size()); + return Context.getFunctionType(ResultType, ParamTypes, EPI); } diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 89064bdcbee..b2da67d997a 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -238,8 +238,14 @@ void ASTTypeWriter::VisitFunctionProtoType(const FunctionProtoType *T) { for (unsigned I = 0, N = T->getNumParams(); I != N; ++I) Writer.AddTypeRef(T->getParamType(I), Record); + if (T->hasExtParameterInfos()) { + for (unsigned I = 0, N = T->getNumParams(); I != N; ++I) + Record.push_back(T->getExtParameterInfo(I).getOpaqueValue()); + } + if (T->isVariadic() || T->hasTrailingReturn() || T->getTypeQuals() || - T->getRefQualifier() || T->getExceptionSpecType() != EST_None) + T->getRefQualifier() || T->getExceptionSpecType() != EST_None || + T->hasExtParameterInfos()) AbbrevToUse = 0; Code = TYPE_FUNCTION_PROTO; From 29d52ea00219e1efa0d961ca396f02a82cc7f364 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 1 Mar 2016 02:09:20 +0000 Subject: [PATCH 416/742] Add an llvm_unreachable back to the autogeneration of this covered switch. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262288 91177308-0d34-0410-b5e6-96231b3b80d8 --- utils/TableGen/ClangAttrEmitter.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/utils/TableGen/ClangAttrEmitter.cpp b/utils/TableGen/ClangAttrEmitter.cpp index e2cabe67645..165f9e6440b 100644 --- a/utils/TableGen/ClangAttrEmitter.cpp +++ b/utils/TableGen/ClangAttrEmitter.cpp @@ -2281,7 +2281,8 @@ void EmitClangAttrASTVisitor(RecordKeeper &Records, raw_ostream &OS) { << " return getDerived().Traverse" << R.getName() << "Attr(" << "cast<" << R.getName() << "Attr>(A));\n"; } - OS << " }\n"; // end case + OS << " }\n"; // end switch + OS << " llvm_unreachable(\"bad attribute kind\");\n"; OS << "}\n"; // end function OS << "#endif // ATTR_VISITOR_DECLS_ONLY\n"; } From a218fd5d5a03e25347c6d7bce2364938dcadb5aa Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 1 Mar 2016 02:09:25 +0000 Subject: [PATCH 417/742] Fix the template instantiation of ExtParameterInfos; tests to follow. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262289 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Type.h | 5 ++ include/clang/Sema/Sema.h | 24 +++++++++- lib/Sema/SemaTemplateDeduction.cpp | 14 ++++-- lib/Sema/SemaTemplateInstantiate.cpp | 9 ++-- lib/Sema/SemaTemplateInstantiateDecl.cpp | 5 +- lib/Sema/TreeTransform.h | 60 ++++++++++++++++++++---- 6 files changed, 97 insertions(+), 20 deletions(-) diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 7b04058d7f9..0cd7e11e28f 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -3355,6 +3355,11 @@ class FunctionProtoType : public FunctionType, public llvm::FoldingSetNode { return ArrayRef(getExtParameterInfosBuffer(), getNumParams()); } + const ExtParameterInfo *getExtParameterInfosOrNull() const { + if (!hasExtParameterInfos()) + return nullptr; + return getExtParameterInfosBuffer(); + } ExtParameterInfo getExtParameterInfo(unsigned I) const { assert(I < getNumParams() && "parameter index out of range"); diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index ebdffc6f19f..b4e908c3baa 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -6980,6 +6980,26 @@ class Sema { SavedPendingLocalImplicitInstantiations; }; + class ExtParameterInfoBuilder { + SmallVector Infos; + bool HasInteresting = false; + + public: + void set(unsigned index, FunctionProtoType::ExtParameterInfo info) { + assert(Infos.size() <= index); + Infos.resize(index); + Infos.push_back(info); + + if (!HasInteresting) + HasInteresting = (info != FunctionProtoType::ExtParameterInfo()); + } + + const FunctionProtoType::ExtParameterInfo * + getPointerOrNull(unsigned numParams) { + return (HasInteresting ? Infos.data() : nullptr); + } + }; + void PerformPendingInstantiations(bool LocalOnly = false); TypeSourceInfo *SubstType(TypeSourceInfo *T, @@ -7009,9 +7029,11 @@ class Sema { bool ExpectParameterPack); bool SubstParmTypes(SourceLocation Loc, ParmVarDecl **Params, unsigned NumParams, + const FunctionProtoType::ExtParameterInfo *ExtParamInfos, const MultiLevelTemplateArgumentList &TemplateArgs, SmallVectorImpl &ParamTypes, - SmallVectorImpl *OutParams = nullptr); + SmallVectorImpl *OutParams, + ExtParameterInfoBuilder &ParamInfos); ExprResult SubstExpr(Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs); diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index 71faafc6bc1..16e2d9f365d 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -2564,6 +2564,8 @@ Sema::SubstituteExplicitTemplateArguments( // Isolate our substituted parameters from our caller. LocalInstantiationScope InstScope(*this, /*MergeWithOuterScope*/true); + ExtParameterInfoBuilder ExtParamInfos; + // Instantiate the types of each of the function parameters given the // explicitly-specified template arguments. If the function has a trailing // return type, substitute it after the arguments to ensure we substitute @@ -2571,8 +2573,9 @@ Sema::SubstituteExplicitTemplateArguments( if (Proto->hasTrailingReturn()) { if (SubstParmTypes(Function->getLocation(), Function->param_begin(), Function->getNumParams(), + Proto->getExtParameterInfosOrNull(), MultiLevelTemplateArgumentList(*ExplicitArgumentList), - ParamTypes)) + ParamTypes, /*params*/ nullptr, ExtParamInfos)) return TDK_SubstitutionFailure; } @@ -2602,21 +2605,24 @@ Sema::SubstituteExplicitTemplateArguments( if (ResultType.isNull() || Trap.hasErrorOccurred()) return TDK_SubstitutionFailure; } - + // Instantiate the types of each of the function parameters given the // explicitly-specified template arguments if we didn't do so earlier. if (!Proto->hasTrailingReturn() && SubstParmTypes(Function->getLocation(), Function->param_begin(), Function->getNumParams(), + Proto->getExtParameterInfosOrNull(), MultiLevelTemplateArgumentList(*ExplicitArgumentList), - ParamTypes)) + ParamTypes, /*params*/ nullptr, ExtParamInfos)) return TDK_SubstitutionFailure; if (FunctionType) { + auto EPI = Proto->getExtProtoInfo(); + EPI.ExtParameterInfos = ExtParamInfos.getPointerOrNull(ParamTypes.size()); *FunctionType = BuildFunctionType(ResultType, ParamTypes, Function->getLocation(), Function->getDeclName(), - Proto->getExtProtoInfo()); + EPI); if (FunctionType->isNull() || Trap.hasErrorOccurred()) return TDK_SubstitutionFailure; } diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index fb7fc109d2e..000eb6f06d6 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -1720,9 +1720,11 @@ ParmVarDecl *Sema::SubstParmVarDecl(ParmVarDecl *OldParm, /// from such a substitution. bool Sema::SubstParmTypes(SourceLocation Loc, ParmVarDecl **Params, unsigned NumParams, + const FunctionProtoType::ExtParameterInfo *ExtParamInfos, const MultiLevelTemplateArgumentList &TemplateArgs, SmallVectorImpl &ParamTypes, - SmallVectorImpl *OutParams) { + SmallVectorImpl *OutParams, + ExtParameterInfoBuilder &ParamInfos) { assert(!ActiveTemplateInstantiations.empty() && "Cannot perform an instantiation without some context on the " "instantiation stack"); @@ -1730,8 +1732,9 @@ bool Sema::SubstParmTypes(SourceLocation Loc, TemplateInstantiator Instantiator(*this, TemplateArgs, Loc, DeclarationName()); return Instantiator.TransformFunctionTypeParams(Loc, Params, NumParams, - nullptr, ParamTypes, - OutParams); + nullptr, ExtParamInfos, + ParamTypes, OutParams, + ParamInfos); } /// \brief Perform substitution on the base class specifiers of the diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 7a452af7783..59c636ce9b2 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -3118,9 +3118,10 @@ TemplateDeclInstantiator::SubstFunctionType(FunctionDecl *D, // In this case, we'll just go instantiate the ParmVarDecls that we // synthesized in the method declaration. SmallVector ParamTypes; + Sema::ExtParameterInfoBuilder ExtParamInfos; if (SemaRef.SubstParmTypes(D->getLocation(), D->param_begin(), - D->getNumParams(), TemplateArgs, ParamTypes, - &Params)) + D->getNumParams(), nullptr, TemplateArgs, + ParamTypes, &Params, ExtParamInfos)) return nullptr; } diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index 60c661c4547..51e27d3663e 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -607,8 +607,10 @@ class TreeTransform { bool TransformFunctionTypeParams(SourceLocation Loc, ParmVarDecl **Params, unsigned NumParams, const QualType *ParamTypes, + const FunctionProtoType::ExtParameterInfo *ParamInfos, SmallVectorImpl &PTypes, - SmallVectorImpl *PVars); + SmallVectorImpl *PVars, + Sema::ExtParameterInfoBuilder &PInfos); /// \brief Transforms a single function-type parameter. Return null /// on error. @@ -4608,8 +4610,10 @@ bool TreeTransform:: TransformFunctionTypeParams(SourceLocation Loc, ParmVarDecl **Params, unsigned NumParams, const QualType *ParamTypes, + const FunctionProtoType::ExtParameterInfo *ParamInfos, SmallVectorImpl &OutParamTypes, - SmallVectorImpl *PVars) { + SmallVectorImpl *PVars, + Sema::ExtParameterInfoBuilder &PInfos) { int indexAdjustment = 0; for (unsigned i = 0; i != NumParams; ++i) { @@ -4658,6 +4662,8 @@ bool TreeTransform:: if (!NewParm) return true; + if (ParamInfos) + PInfos.set(OutParamTypes.size(), ParamInfos[i]); OutParamTypes.push_back(NewParm->getType()); if (PVars) PVars->push_back(NewParm); @@ -4675,6 +4681,8 @@ bool TreeTransform:: if (!NewParm) return true; + if (ParamInfos) + PInfos.set(OutParamTypes.size(), ParamInfos[i]); OutParamTypes.push_back(NewParm->getType()); if (PVars) PVars->push_back(NewParm); @@ -4705,6 +4713,8 @@ bool TreeTransform:: if (!NewParm) return true; + if (ParamInfos) + PInfos.set(OutParamTypes.size(), ParamInfos[i]); OutParamTypes.push_back(NewParm->getType()); if (PVars) PVars->push_back(NewParm); @@ -4744,6 +4754,8 @@ bool TreeTransform:: if (NewType.isNull()) return true; + if (ParamInfos) + PInfos.set(OutParamTypes.size(), ParamInfos[i]); OutParamTypes.push_back(NewType); if (PVars) PVars->push_back(nullptr); @@ -4761,6 +4773,8 @@ bool TreeTransform:: if (NewType.isNull()) return true; + if (ParamInfos) + PInfos.set(OutParamTypes.size(), ParamInfos[i]); OutParamTypes.push_back(NewType); if (PVars) PVars->push_back(nullptr); @@ -4783,6 +4797,8 @@ bool TreeTransform:: NewType = getSema().Context.getPackExpansionType(NewType, NumExpansions); + if (ParamInfos) + PInfos.set(OutParamTypes.size(), ParamInfos[i]); OutParamTypes.push_back(NewType); if (PVars) PVars->push_back(nullptr); @@ -4817,6 +4833,7 @@ template template QualType TreeTransform::TransformFunctionProtoType( TypeLocBuilder &TLB, FunctionProtoTypeLoc TL, CXXRecordDecl *ThisContext, unsigned ThisTypeQuals, Fn TransformExceptionSpec) { + // Transform the parameters and return type. // // We are required to instantiate the params and return type in source order. @@ -4826,6 +4843,7 @@ QualType TreeTransform::TransformFunctionProtoType( // SmallVector ParamTypes; SmallVector ParamDecls; + Sema::ExtParameterInfoBuilder ExtParamInfos; const FunctionProtoType *T = TL.getTypePtr(); QualType ResultType; @@ -4833,7 +4851,9 @@ QualType TreeTransform::TransformFunctionProtoType( if (T->hasTrailingReturn()) { if (getDerived().TransformFunctionTypeParams( TL.getBeginLoc(), TL.getParmArray(), TL.getNumParams(), - TL.getTypePtr()->param_type_begin(), ParamTypes, &ParamDecls)) + TL.getTypePtr()->param_type_begin(), + T->getExtParameterInfosOrNull(), + ParamTypes, &ParamDecls, ExtParamInfos)) return QualType(); { @@ -4857,7 +4877,9 @@ QualType TreeTransform::TransformFunctionProtoType( if (getDerived().TransformFunctionTypeParams( TL.getBeginLoc(), TL.getParmArray(), TL.getNumParams(), - TL.getTypePtr()->param_type_begin(), ParamTypes, &ParamDecls)) + TL.getTypePtr()->param_type_begin(), + T->getExtParameterInfosOrNull(), + ParamTypes, &ParamDecls, ExtParamInfos)) return QualType(); } @@ -4867,8 +4889,19 @@ QualType TreeTransform::TransformFunctionProtoType( if (TransformExceptionSpec(EPI.ExceptionSpec, EPIChanged)) return QualType(); - // FIXME: Need to transform ConsumedParameters for variadic template - // expansion. + // Handle extended parameter information. + if (auto NewExtParamInfos = + ExtParamInfos.getPointerOrNull(ParamTypes.size())) { + if (!EPI.ExtParameterInfos || + llvm::makeArrayRef(EPI.ExtParameterInfos, TL.getNumParams()) + != llvm::makeArrayRef(NewExtParamInfos, ParamTypes.size())) { + EPIChanged = true; + } + EPI.ExtParameterInfos = NewExtParamInfos; + } else if (EPI.ExtParameterInfos) { + EPIChanged = true; + EPI.ExtParameterInfos = nullptr; + } QualType Result = TL.getType(); if (getDerived().AlwaysRebuild() || ResultType != T->getReturnType() || @@ -11063,22 +11096,29 @@ TreeTransform::TransformBlockExpr(BlockExpr *E) { SmallVector params; SmallVector paramTypes; + const FunctionProtoType *exprFunctionType = E->getFunctionType(); + // Parameter substitution. + Sema::ExtParameterInfoBuilder extParamInfos; if (getDerived().TransformFunctionTypeParams(E->getCaretLocation(), oldBlock->param_begin(), oldBlock->param_size(), - nullptr, paramTypes, ¶ms)) { + nullptr, + exprFunctionType->getExtParameterInfosOrNull(), + paramTypes, ¶ms, + extParamInfos)) { getSema().ActOnBlockError(E->getCaretLocation(), /*Scope=*/nullptr); return ExprError(); } - const FunctionProtoType *exprFunctionType = E->getFunctionType(); QualType exprResultType = getDerived().TransformType(exprFunctionType->getReturnType()); + auto epi = exprFunctionType->getExtProtoInfo(); + epi.ExtParameterInfos = extParamInfos.getPointerOrNull(paramTypes.size()); + QualType functionType = - getDerived().RebuildFunctionProtoType(exprResultType, paramTypes, - exprFunctionType->getExtProtoInfo()); + getDerived().RebuildFunctionProtoType(exprResultType, paramTypes, epi); blockScope->FunctionType = functionType; // Set the parameters on the block decl. From 4c9f6b2ccb899d5eb901d1647908b8438436bc7e Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 1 Mar 2016 06:27:40 +0000 Subject: [PATCH 418/742] Better comments for ExtParameterInfo. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262308 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Type.h | 24 ++++++++++++++++++++++++ include/clang/Sema/Sema.h | 11 +++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 0cd7e11e28f..4e26236024b 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -3040,6 +3040,25 @@ class FunctionNoProtoType : public FunctionType, public llvm::FoldingSetNode { /// type. class FunctionProtoType : public FunctionType, public llvm::FoldingSetNode { public: + /// Interesting information about a specific parameter that can't simply + /// be reflected in parameter's type. + /// + /// It makes sense to model language features this way when there's some + /// sort of parameter-specific override (such as an attribute) that + /// affects how the function is called. For example, the ARC ns_consumed + /// attribute changes whether a parameter is passed at +0 (the default) + /// or +1 (ns_consumed). This must be reflected in the function type, + /// but isn't really a change to the parameter type. + /// + /// One serious disadvantage of modelling language features this way is + /// that they generally do not work with language features that attempt + /// to destructure types. For example, template argument deduction will + /// not be able to match a parameter declared as + /// T (*)(U) + /// against an argument of type + /// void (*)(__attribute__((ns_consumed)) id) + /// because the substitution of T=void, U=id into the former will + /// not produce the latter. class ExtParameterInfo { enum { IsConsumed = 0x01, @@ -3349,12 +3368,17 @@ class FunctionProtoType : public FunctionType, public llvm::FoldingSetNode { return exception_begin() + NumExceptions; } + /// Is there any interesting extra information for any of the parameters + /// of this function type? bool hasExtParameterInfos() const { return HasExtParameterInfos; } ArrayRef getExtParameterInfos() const { assert(hasExtParameterInfos()); return ArrayRef(getExtParameterInfosBuffer(), getNumParams()); } + /// Return a pointer to the beginning of the array of extra parameter + /// information, if present, or else null if none of the parameters + /// carry it. This is equivalent to getExtProtoInfo().ExtParameterInfos. const ExtParameterInfo *getExtParameterInfosOrNull() const { if (!hasExtParameterInfos()) return nullptr; diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index b4e908c3baa..524ddfb989e 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -6980,11 +6980,14 @@ class Sema { SavedPendingLocalImplicitInstantiations; }; + /// A helper class for building up ExtParameterInfos. class ExtParameterInfoBuilder { - SmallVector Infos; + SmallVector Infos; bool HasInteresting = false; public: + /// Set the ExtParameterInfo for the parameter at the given index, + /// void set(unsigned index, FunctionProtoType::ExtParameterInfo info) { assert(Infos.size() <= index); Infos.resize(index); @@ -6994,9 +6997,13 @@ class Sema { HasInteresting = (info != FunctionProtoType::ExtParameterInfo()); } + /// Return a pointer (suitable for setting in an ExtProtoInfo) to the + /// ExtParameterInfo array we've built up. const FunctionProtoType::ExtParameterInfo * getPointerOrNull(unsigned numParams) { - return (HasInteresting ? Infos.data() : nullptr); + if (!HasInteresting) return nullptr; + Infos.resize(numParams); + return Infos.data(); } }; From 399962bcf29434e123986fb405de9a4b5cee32b7 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 1 Mar 2016 06:54:30 +0000 Subject: [PATCH 419/742] Test template instantiation of ns_consumed and ns_returns_retained. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262311 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/SemaObjCXX/arc-templates.mm | 126 ++++++++++++++++++++++++++++++- 1 file changed, 125 insertions(+), 1 deletion(-) diff --git a/test/SemaObjCXX/arc-templates.mm b/test/SemaObjCXX/arc-templates.mm index ebede6404f9..81425985e62 100644 --- a/test/SemaObjCXX/arc-templates.mm +++ b/test/SemaObjCXX/arc-templates.mm @@ -1,4 +1,7 @@ -// RUN: %clang_cc1 -fobjc-runtime-has-weak -fsyntax-only -fobjc-arc -verify -fblocks %s +// RUN: %clang_cc1 -fobjc-runtime-has-weak -fsyntax-only -fobjc-arc -verify -fblocks -std=c++11 %s + +#define CONSUMED __attribute__((ns_consumed)) +#define PRODUCED __attribute__((ns_returns_retained)) @interface A @end @@ -318,3 +321,124 @@ void foo() { double &dr = (f)(unsafe); } } + +namespace consumed { + void take_yes_no(void (&)(id CONSUMED, id)); // expected-note 2 {{candidate function not viable}} + void take_no_yes(void (&)(id, CONSUMED id)); // expected-note 2 {{candidate function not viable}} + void take_yes_yes(void (&)(CONSUMED id, CONSUMED id)); // expected-note 2 {{candidate function not viable}} + + template void consumes_first(id CONSUMED, As...); + void test1() { + take_yes_no(consumes_first); + take_no_yes(consumes_first); // expected-error {{no matching function}} + take_yes_yes(consumes_first); // expected-error {{no matching function}} + } + + template void consumes_rest(id, CONSUMED As...); + void test2() { + take_yes_no(consumes_rest); // expected-error {{no matching function}} + take_no_yes(consumes_rest); + take_yes_yes(consumes_rest); // expected-error {{no matching function}} + } + + template void consumes_two(CONSUMED T, CONSUMED U); + void test3() { + take_yes_no(consumes_two); // expected-error {{no matching function}} + take_no_yes(consumes_two); // expected-error {{no matching function}} + take_yes_yes(consumes_two); + } +} + +namespace consumed_nested { + void take_yes_no(void (&)(id CONSUMED, id)); // expected-note 4 {{candidate function not viable}} + void take_no_yes(void (&)(id, CONSUMED id)); // expected-note 4 {{candidate function not viable}} + void take_yes_yes(void (&)(CONSUMED id, CONSUMED id)); // expected-note 4 {{candidate function not viable}} + + template struct consumes_first { + template static void fn(id CONSUMED, As...); + }; + void test1() { + take_yes_no(consumes_first<1>::fn); + take_no_yes(consumes_first<2>::fn); // expected-error {{no matching function}} + take_yes_yes(consumes_first<3>::fn); // expected-error {{no matching function}} + take_yes_no(consumes_first<4>::fn); + take_no_yes(consumes_first<5>::fn); // expected-error {{no matching function}} + take_yes_yes(consumes_first<6>::fn); // expected-error {{no matching function}} + } + + template struct consumes_rest { + template static void fn(id, CONSUMED As...); + }; + void test2() { + take_yes_no(consumes_rest<1>::fn); // expected-error {{no matching function}} + take_no_yes(consumes_rest<2>::fn); + take_yes_yes(consumes_rest<3>::fn); // expected-error {{no matching function}} + take_yes_no(consumes_rest<4>::fn); // expected-error {{no matching function}} + take_no_yes(consumes_rest<5>::fn); + take_yes_yes(consumes_rest<6>::fn); // expected-error {{no matching function}} + } + + template struct consumes_two { + template static void fn(CONSUMED T, CONSUMED U); + }; + void test3() { + take_yes_no(consumes_two<1>::fn); // expected-error {{no matching function}} + take_no_yes(consumes_two<2>::fn); // expected-error {{no matching function}} + take_yes_yes(consumes_two<3>::fn); + take_yes_no(consumes_two<1>::fn); // expected-error {{no matching function}} + take_no_yes(consumes_two<2>::fn); // expected-error {{no matching function}} + take_yes_yes(consumes_two<3>::fn); + } +} + +namespace produced { + void take_yes(PRODUCED id (&)()); // expected-note 2 {{candidate function not viable}} + void take_no(id (&)()); // expected-note 2 {{candidate function not viable}} + + template T non_produces1(); + template T non_produces2(); + template T non_produces3(); + template T non_produces4(); + void test1() { + take_yes(non_produces1); // expected-error {{no matching function}} + take_yes(non_produces2); // expected-error {{no matching function}} + take_no(non_produces3); + take_no(non_produces4); + } + + template PRODUCED T produces1(); + template PRODUCED T produces2(); + template PRODUCED T produces3(); + template PRODUCED T produces4(); + void test2() { + take_yes(produces1); + take_yes(produces2); + take_no(produces3); // expected-error {{no matching function}} + take_no(produces4); // expected-error {{no matching function}} + } +} + +namespace produced_nested { + void take_yes(PRODUCED id (&)()); // expected-note 2 {{candidate function not viable}} + void take_no(id (&)()); // expected-note 2 {{candidate function not viable}} + + template struct non_produces { + template static T fn(); + }; + void test1() { + take_yes(non_produces<1>::fn); // expected-error {{no matching function}} + take_yes(non_produces<2>::fn); // expected-error {{no matching function}} + take_no(non_produces<3>::fn); + take_no(non_produces<4>::fn); + } + + template struct produces { + template static PRODUCED T fn(); + }; + void test2() { + take_yes(produces<1>::fn); + take_yes(produces<2>::fn); + take_no(produces<3>::fn); // expected-error {{no matching function}} + take_no(produces<4>::fn); // expected-error {{no matching function}} + } +} From 9152050f64f21dc0d3be1cb8f2dbfaf19cc030c8 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 1 Mar 2016 22:18:03 +0000 Subject: [PATCH 420/742] Mangle extended qualifiers in the proper order and mangle the ARC ownership-convention function type modifications. According to the Itanium ABI, vendor extended qualifiers are supposed to be mangled in reverse-alphabetical order before any CVR qualifiers. The ARC function type conventions are plausibly order-significant (they are associated with the function type), which permits us to ignore the need to correctly inter-order them with any other vendor qualifiers on the parameter and return types. Implementing these rules correctly is technically an ABI break. Apple is comfortable with the risk of incompatibility here for the ARC features, and I believe that address-space qualification is still uncommon enough to allow us to adopt the conforming rule without serious risk. Still, targets which make heavy use of address space qualification may want to revert to the non-conforming order. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262414 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ItaniumMangle.cpp | 124 ++++++++++++++++++----- test/CodeGenCXX/mangle-address-space.cpp | 3 + test/CodeGenObjCXX/arc-attrs.mm | 10 +- test/CodeGenObjCXX/arc-mangle.mm | 13 ++- test/CodeGenObjCXX/arc-move.mm | 4 +- 5 files changed, 120 insertions(+), 34 deletions(-) diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index 3f6b682f238..269278b96dd 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -364,6 +364,7 @@ class CXXNameMangler { StringRef Prefix = ""); void mangleOperatorName(DeclarationName Name, unsigned Arity); void mangleOperatorName(OverloadedOperatorKind OO, unsigned Arity); + void mangleVendorQualifier(StringRef qualifier); void mangleQualifiers(Qualifiers Quals); void mangleRefQualifier(RefQualifierKind RefQualifier); @@ -377,7 +378,10 @@ class CXXNameMangler { void mangleType(const TagType*); void mangleType(TemplateName); - void mangleBareFunctionType(const FunctionType *T, bool MangleReturnType, + static StringRef getCallingConvQualifierName(CallingConv CC); + void mangleExtParameterInfo(FunctionProtoType::ExtParameterInfo info); + void mangleExtFunctionInfo(const FunctionType *T); + void mangleBareFunctionType(const FunctionProtoType *T, bool MangleReturnType, const FunctionDecl *FD = nullptr); void mangleNeonVectorType(const VectorType *T); void mangleAArch64NeonVectorType(const VectorType *T); @@ -523,7 +527,7 @@ void CXXNameMangler::mangleFunctionEncoding(const FunctionDecl *FD) { FD = PrimaryTemplate->getTemplatedDecl(); } - mangleBareFunctionType(FD->getType()->getAs(), + mangleBareFunctionType(FD->getType()->castAs(), MangleReturnType, FD); } @@ -1767,14 +1771,9 @@ CXXNameMangler::mangleOperatorName(OverloadedOperatorKind OO, unsigned Arity) { } void CXXNameMangler::mangleQualifiers(Qualifiers Quals) { - // ::= [r] [V] [K] # restrict (C99), volatile, const - if (Quals.hasRestrict()) - Out << 'r'; - if (Quals.hasVolatile()) - Out << 'V'; - if (Quals.hasConst()) - Out << 'K'; + // Vendor qualifiers come first. + // Address space qualifiers start with an ordinary letter. if (Quals.hasAddressSpace()) { // Address space extension: // @@ -1802,10 +1801,10 @@ void CXXNameMangler::mangleQualifiers(Qualifiers Quals) { case LangAS::cuda_shared: ASString = "CUshared"; break; } } - Out << 'U' << ASString.size() << ASString; + mangleVendorQualifier(ASString); } - - StringRef LifetimeName; + + // The ARC ownership qualifiers start with underscores. switch (Quals.getObjCLifetime()) { // Objective-C ARC Extension: // @@ -1816,15 +1815,15 @@ void CXXNameMangler::mangleQualifiers(Qualifiers Quals) { break; case Qualifiers::OCL_Weak: - LifetimeName = "__weak"; + mangleVendorQualifier("__weak"); break; case Qualifiers::OCL_Strong: - LifetimeName = "__strong"; + mangleVendorQualifier("__strong"); break; case Qualifiers::OCL_Autoreleasing: - LifetimeName = "__autoreleasing"; + mangleVendorQualifier("__autoreleasing"); break; case Qualifiers::OCL_ExplicitNone: @@ -1837,8 +1836,18 @@ void CXXNameMangler::mangleQualifiers(Qualifiers Quals) { // in any type signatures that need to be mangled. break; } - if (!LifetimeName.empty()) - Out << 'U' << LifetimeName.size() << LifetimeName; + + // ::= [r] [V] [K] # restrict (C99), volatile, const + if (Quals.hasRestrict()) + Out << 'r'; + if (Quals.hasVolatile()) + Out << 'V'; + if (Quals.hasConst()) + Out << 'K'; +} + +void CXXNameMangler::mangleVendorQualifier(StringRef name) { + Out << 'U' << name.size() << name; } void CXXNameMangler::mangleRefQualifier(RefQualifierKind RefQualifier) { @@ -2137,10 +2146,63 @@ void CXXNameMangler::mangleType(const BuiltinType *T) { } } +StringRef CXXNameMangler::getCallingConvQualifierName(CallingConv CC) { + switch (CC) { + case CC_C: + return ""; + + case CC_X86StdCall: + case CC_X86FastCall: + case CC_X86ThisCall: + case CC_X86VectorCall: + case CC_X86Pascal: + case CC_X86_64Win64: + case CC_X86_64SysV: + case CC_AAPCS: + case CC_AAPCS_VFP: + case CC_IntelOclBicc: + case CC_SpirFunction: + case CC_SpirKernel: + // FIXME: we should be mangling all of the above. + return ""; + } + llvm_unreachable("bad calling convention"); +} + +void CXXNameMangler::mangleExtFunctionInfo(const FunctionType *T) { + // Fast path. + if (T->getExtInfo() == FunctionType::ExtInfo()) + return; + + // Vendor-specific qualifiers are emitted in reverse alphabetical order. + // This will get more complicated in the future if we mangle other + // things here; but for now, since we mangle ns_returns_retained as + // a qualifier on the result type, we can get away with this: + StringRef CCQualifier = getCallingConvQualifierName(T->getExtInfo().getCC()); + if (!CCQualifier.empty()) + mangleVendorQualifier(CCQualifier); + + // FIXME: regparm + // FIXME: noreturn +} + +void +CXXNameMangler::mangleExtParameterInfo(FunctionProtoType::ExtParameterInfo PI) { + // Vendor-specific qualifiers are emitted in reverse alphabetical order. + + // Note that these are *not* substitution candidates. Demanglers might + // have trouble with this if the parameter type is fully substituted. + + if (PI.isConsumed()) + Out << "U11ns_consumed"; +} + // ::= // ::= [] F [Y] // [] E void CXXNameMangler::mangleType(const FunctionProtoType *T) { + mangleExtFunctionInfo(T); + // Mangle CV-qualifiers, if present. These are 'this' qualifiers, // e.g. "const" in "int (A::*)() const". mangleQualifiers(Qualifiers::fromCVRMask(T->getTypeQuals())); @@ -2173,12 +2235,9 @@ void CXXNameMangler::mangleType(const FunctionNoProtoType *T) { Out << 'E'; } -void CXXNameMangler::mangleBareFunctionType(const FunctionType *T, +void CXXNameMangler::mangleBareFunctionType(const FunctionProtoType *Proto, bool MangleReturnType, const FunctionDecl *FD) { - // We should never be mangling something without a prototype. - const FunctionProtoType *Proto = cast(T); - // Record that we're in a function type. See mangleFunctionParam // for details on what we're trying to achieve here. FunctionTypeDepthState saved = FunctionTypeDepth.push(); @@ -2186,7 +2245,20 @@ void CXXNameMangler::mangleBareFunctionType(const FunctionType *T, // ::= + if (MangleReturnType) { FunctionTypeDepth.enterResultType(); - mangleType(Proto->getReturnType()); + + // Mangle ns_returns_retained as an order-sensitive qualifier here. + if (Proto->getExtInfo().getProducesResult()) + mangleVendorQualifier("ns_returns_retained"); + + // Mangle the return type without any direct ARC ownership qualifiers. + QualType ReturnTy = Proto->getReturnType(); + if (ReturnTy.getObjCLifetime()) { + auto SplitReturnTy = ReturnTy.split(); + SplitReturnTy.Quals.removeObjCLifetime(); + ReturnTy = getASTContext().getQualifiedType(SplitReturnTy); + } + mangleType(ReturnTy); + FunctionTypeDepth.leaveResultType(); } @@ -2200,7 +2272,13 @@ void CXXNameMangler::mangleBareFunctionType(const FunctionType *T, assert(!FD || FD->getNumParams() == Proto->getNumParams()); for (unsigned I = 0, E = Proto->getNumParams(); I != E; ++I) { - const auto &ParamTy = Proto->getParamType(I); + // Mangle extended parameter info as order-sensitive qualifiers here. + if (Proto->hasExtParameterInfos()) { + mangleExtParameterInfo(Proto->getExtParameterInfo(I)); + } + + // Mangle the type. + QualType ParamTy = Proto->getParamType(I); mangleType(Context.getASTContext().getSignatureParameterType(ParamTy)); if (FD) { diff --git a/test/CodeGenCXX/mangle-address-space.cpp b/test/CodeGenCXX/mangle-address-space.cpp index f18480de83d..cd10384594e 100644 --- a/test/CodeGenCXX/mangle-address-space.cpp +++ b/test/CodeGenCXX/mangle-address-space.cpp @@ -10,3 +10,6 @@ typedef OpaqueType __attribute__((address_space(100))) * OpaqueTypePtr; // CHECK-LABEL: define {{.*}}void @_Z2f0PU5AS10010OpaqueType void f0(OpaqueTypePtr) { } + +// CHECK-LABEL: define {{.*}}void @_Z2f1PU3AS1Kc +void f1(char __attribute__((address_space(1))) const *p) {} \ No newline at end of file diff --git a/test/CodeGenObjCXX/arc-attrs.mm b/test/CodeGenObjCXX/arc-attrs.mm index 0f0610f1721..d5716777193 100644 --- a/test/CodeGenObjCXX/arc-attrs.mm +++ b/test/CodeGenObjCXX/arc-attrs.mm @@ -12,7 +12,7 @@ void sanityTest() { id x = makeObject1(); // CHECK-NEXT: [[OBJ2:%.*]] = call i8* @_Z11makeObject2v() - // CHECK-NEXT: call void @_Z13releaseObjectP11objc_object(i8* [[OBJ2]]) + // CHECK-NEXT: call void @_Z13releaseObjectU11ns_consumedP11objc_object(i8* [[OBJ2]]) releaseObject(makeObject2()); // CHECK-NEXT: call void @objc_storeStrong(i8** [[X]], i8* null) @@ -31,16 +31,16 @@ void sanityTest() { // CHECK-LABEL: define void @_Z12templateTestv void templateTest() { // CHECK: [[X:%.*]] = alloca i8*, align 8 - // CHECK-NEXT: [[OBJ1:%.*]] = call i8* @_Z12makeObjectT1IU8__strongP11objc_objectET_v() + // CHECK-NEXT: [[OBJ1:%.*]] = call i8* @_Z12makeObjectT1IU8__strongP11objc_objectEU19ns_returns_retainedT_v() // CHECK-NEXT: store i8* [[OBJ1]], i8** [[X]], align 8 id x = makeObjectT1(); - // CHECK-NEXT: [[OBJ2:%.*]] = call i8* @_Z12makeObjectT2IU8__strongP11objc_objectET_v() - // CHECK-NEXT: call void @_Z13releaseObjectP11objc_object(i8* [[OBJ2]]) + // CHECK-NEXT: [[OBJ2:%.*]] = call i8* @_Z12makeObjectT2IU8__strongP11objc_objectEU19ns_returns_retainedT_v() + // CHECK-NEXT: call void @_Z13releaseObjectU11ns_consumedP11objc_object(i8* [[OBJ2]]) releaseObject(makeObjectT2()); // CHECK-NEXT: [[OBJ3:%.*]] = call i8* @_Z11makeObject1v() - // CHECK-NEXT: call void @_Z14releaseObjectTIU8__strongP11objc_objectEvT_(i8* [[OBJ3]]) + // CHECK-NEXT: call void @_Z14releaseObjectTIU8__strongP11objc_objectEvU11ns_consumedT_(i8* [[OBJ3]]) releaseObjectT(makeObject1()); // CHECK-NEXT: call void @objc_storeStrong(i8** [[X]], i8* null) diff --git a/test/CodeGenObjCXX/arc-mangle.mm b/test/CodeGenObjCXX/arc-mangle.mm index a168d41b336..84acbdb1448 100644 --- a/test/CodeGenObjCXX/arc-mangle.mm +++ b/test/CodeGenObjCXX/arc-mangle.mm @@ -8,15 +8,20 @@ void f(__weak id *) {} void f(__autoreleasing id *) {} // CHECK-LABEL: define {{.*}}void @_Z1fPP11objc_object(i8**) void f(__unsafe_unretained id *) {} -// CHECK-LABEL: define {{.*}}void @_Z1fPKU8__strongP11objc_object(i8**) +// CHECK-LABEL: define {{.*}}void @_Z1fPU8__strongKP11objc_object(i8**) void f(const __strong id *) {} -// CHECK-LABEL: define {{.*}}void @_Z1fPKU6__weakP11objc_object(i8**) +// CHECK-LABEL: define {{.*}}void @_Z1fPU6__weakKP11objc_object(i8**) void f(const __weak id *) {} -// CHECK-LABEL: define {{.*}}void @_Z1fPKU15__autoreleasingP11objc_object(i8**) +// CHECK-LABEL: define {{.*}}void @_Z1fPU15__autoreleasingKP11objc_object(i8**) void f(const __autoreleasing id *) {} // CHECK-LABEL: define {{.*}}void @_Z1fPKP11objc_object(i8**) void f(const __unsafe_unretained id *) {} - +// CHECK-LABEL: define {{.*}}void @_Z1fPFU19ns_returns_retainedP11objc_objectvE +void f(__attribute__((ns_returns_retained)) id (*fn)()) {} +// CHECK-LABEL: define {{.*}}void @_Z1fPFP11objc_objectU11ns_consumedS0_S0_E +void f(id (*fn)(__attribute__((ns_consumed)) id, id)) {} +// CHECK-LABEL: define {{.*}}void @_Z1fPFP11objc_objectS0_U11ns_consumedS0_E +void f(__strong id (*fn)(id, __attribute__((ns_consumed)) id)) {} template struct unsigned_c { }; diff --git a/test/CodeGenObjCXX/arc-move.mm b/test/CodeGenObjCXX/arc-move.mm index 76fb15b290d..d1710e291b0 100644 --- a/test/CodeGenObjCXX/arc-move.mm +++ b/test/CodeGenObjCXX/arc-move.mm @@ -72,10 +72,10 @@ void library_move(__strong id &y) { // CHECK-NEXT: ret void } -// CHECK-LABEL: define void @_Z10const_moveRKU8__strongP11objc_object( +// CHECK-LABEL: define void @_Z10const_moveRU8__strongKP11objc_object( void const_move(const __strong id &x) { // CHECK: [[Y:%.*]] = alloca i8*, - // CHECK: [[X:%.*]] = call dereferenceable({{[0-9]+}}) i8** @_Z4moveIRKU8__strongP11objc_objectEON16remove_referenceIT_E4typeEOS5_( + // CHECK: [[X:%.*]] = call dereferenceable({{[0-9]+}}) i8** @_Z4moveIRU8__strongKP11objc_objectEON16remove_referenceIT_E4typeEOS5_( // CHECK-NEXT: [[T0:%.*]] = load i8*, i8** [[X]] // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retain(i8* [[T0]]) // CHECK-NEXT: store i8* [[T1]], i8** [[Y]] From 89ed6f099868f44afbb35ff9455be7aeaf82d1a3 Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 3 Mar 2016 00:10:03 +0000 Subject: [PATCH 421/742] Improve some infrastructure for extended parameter infos and fix a bug with the instantiation of ns_consumed parameter attributes in ARC. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262551 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 3 ++ include/clang/Sema/AttributeList.h | 35 ++++++++++---- include/clang/Sema/Sema.h | 4 ++ lib/AST/TypePrinter.cpp | 4 ++ lib/Sema/SemaDeclAttr.cpp | 56 +++++++++++++++------- lib/Sema/SemaTemplateInstantiateDecl.cpp | 8 ++++ lib/Sema/SemaType.cpp | 41 +++++++++++++--- test/SemaCXX/cxx11-gnu-attrs.cpp | 1 + test/SemaObjCXX/arc-nsconsumed-errors.mm | 4 +- test/SemaObjCXX/arc-templates.mm | 8 ++++ 10 files changed, 129 insertions(+), 35 deletions(-) diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index cd880f03a89..bfa75d5e446 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -2929,6 +2929,9 @@ def warn_ns_attribute_wrong_return_type : Warning< "%0 attribute only applies to %select{functions|methods|properties}1 that " "return %select{an Objective-C object|a pointer|a non-retainable pointer}2">, InGroup; +def err_ns_attribute_wrong_parameter_type : Error< + "%0 attribute only applies to " + "%select{Objective-C object|pointer|pointer-to-CF-pointer}1 parameters">; def warn_ns_attribute_wrong_parameter_type : Warning< "%0 attribute only applies to " "%select{Objective-C object|pointer|pointer-to-CF-pointer}1 parameters">, diff --git a/include/clang/Sema/AttributeList.h b/include/clang/Sema/AttributeList.h index 1a53a2c3d4a..5e3d8eeec15 100644 --- a/include/clang/Sema/AttributeList.h +++ b/include/clang/Sema/AttributeList.h @@ -94,9 +94,11 @@ class AttributeList { // TODO: This should really be called ParsedAttribute SourceLocation ScopeLoc; SourceLocation EllipsisLoc; + unsigned AttrKind : 16; + /// The number of expression arguments this attribute has. /// The expressions themselves are stored after the object. - unsigned NumArgs : 15; + unsigned NumArgs : 16; /// Corresponds to the Syntax enum. unsigned SyntaxUsed : 3; @@ -122,7 +124,11 @@ class AttributeList { // TODO: This should really be called ParsedAttribute /// True if this has a ParsedType unsigned HasParsedType : 1; - unsigned AttrKind : 8; + /// True if the processing cache is valid. + mutable unsigned HasProcessingCache : 1; + + /// A cached value. + mutable unsigned ProcessingCache : 8; /// \brief The location of the 'unavailable' keyword in an /// availability attribute. @@ -220,7 +226,8 @@ class AttributeList { // TODO: This should really be called ParsedAttribute ScopeLoc(scopeLoc), EllipsisLoc(ellipsisLoc), NumArgs(numArgs), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false), - HasParsedType(false), NextInPosition(nullptr), NextInPool(nullptr) { + HasParsedType(false), HasProcessingCache(false), + NextInPosition(nullptr), NextInPool(nullptr) { if (numArgs) memcpy(getArgsBuffer(), args, numArgs * sizeof(ArgsUnion)); AttrKind = getKind(getName(), getScopeName(), syntaxUsed); } @@ -238,8 +245,8 @@ class AttributeList { // TODO: This should really be called ParsedAttribute ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(1), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(true), IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false), - UnavailableLoc(unavailable), MessageExpr(messageExpr), - NextInPosition(nullptr), NextInPool(nullptr) { + HasProcessingCache(false), UnavailableLoc(unavailable), + MessageExpr(messageExpr), NextInPosition(nullptr), NextInPool(nullptr) { ArgsUnion PVal(Parm); memcpy(getArgsBuffer(), &PVal, sizeof(ArgsUnion)); new (&getAvailabilitySlot(IntroducedSlot)) AvailabilityChange(introduced); @@ -259,7 +266,7 @@ class AttributeList { // TODO: This should really be called ParsedAttribute ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(3), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false), - NextInPosition(nullptr), NextInPool(nullptr) { + HasProcessingCache(false), NextInPosition(nullptr), NextInPool(nullptr) { ArgsVector Args; Args.push_back(Parm1); Args.push_back(Parm2); @@ -277,7 +284,7 @@ class AttributeList { // TODO: This should really be called ParsedAttribute ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(1), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(true), IsProperty(false), HasParsedType(false), - NextInPosition(nullptr), NextInPool(nullptr) { + HasProcessingCache(false), NextInPosition(nullptr), NextInPool(nullptr) { ArgsUnion PVal(ArgKind); memcpy(getArgsBuffer(), &PVal, sizeof(ArgsUnion)); TypeTagForDatatypeData &ExtraData = getTypeTagForDatatypeDataSlot(); @@ -295,7 +302,7 @@ class AttributeList { // TODO: This should really be called ParsedAttribute ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(0), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(true), - NextInPosition(nullptr), NextInPool(nullptr) { + HasProcessingCache(false), NextInPosition(nullptr), NextInPool(nullptr){ new (&getTypeBuffer()) ParsedType(typeArg); AttrKind = getKind(getName(), getScopeName(), syntaxUsed); } @@ -309,7 +316,7 @@ class AttributeList { // TODO: This should really be called ParsedAttribute ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(0), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(true), HasParsedType(false), - NextInPosition(nullptr), NextInPool(nullptr) { + HasProcessingCache(false), NextInPosition(nullptr), NextInPool(nullptr) { new (&getPropertyDataBuffer()) PropertyData(getterId, setterId); AttrKind = getKind(getName(), getScopeName(), syntaxUsed); } @@ -361,6 +368,16 @@ class AttributeList { // TODO: This should really be called ParsedAttribute bool isInvalid() const { return Invalid; } void setInvalid(bool b = true) const { Invalid = b; } + bool hasProcessingCache() const { return HasProcessingCache; } + unsigned getProcessingCache() const { + assert(hasProcessingCache()); + return ProcessingCache; + } + void setProcessingCache(unsigned value) const { + ProcessingCache = value; + HasProcessingCache = true; + } + bool isUsedAsTypeAttr() const { return UsedAsTypeAttr; } void setUsedAsTypeAttr() { UsedAsTypeAttr = true; } diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 524ddfb989e..e6b66b9d1ad 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -7797,6 +7797,10 @@ class Sema { void AddLaunchBoundsAttr(SourceRange AttrRange, Decl *D, Expr *MaxThreads, Expr *MinBlocks, unsigned SpellingListIndex); + void AddNSConsumedAttr(SourceRange AttrRange, Decl *D, + unsigned SpellingListIndex, bool isNSConsumed, + bool isTemplateInstantiation); + //===--------------------------------------------------------------------===// // C++ Coroutines TS // diff --git a/lib/AST/TypePrinter.cpp b/lib/AST/TypePrinter.cpp index 762033b600e..3b96cc5f892 100644 --- a/lib/AST/TypePrinter.cpp +++ b/lib/AST/TypePrinter.cpp @@ -641,6 +641,10 @@ void TypePrinter::printFunctionProtoAfter(const FunctionProtoType *T, ParamPolicyRAII ParamPolicy(Policy); for (unsigned i = 0, e = T->getNumParams(); i != e; ++i) { if (i) OS << ", "; + + auto EPI = T->getExtParameterInfo(i); + if (EPI.isConsumed()) OS << "__attribute__((ns_consumed)) "; + print(T->getParamType(i), OS, StringRef()); } } diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 9f03accbf0b..5d74e64d662 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -3778,6 +3778,11 @@ bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC, if (attr.isInvalid()) return true; + if (attr.hasProcessingCache()) { + CC = (CallingConv) attr.getProcessingCache(); + return false; + } + unsigned ReqArgs = attr.getKind() == AttributeList::AT_Pcs ? 1 : 0; if (!checkAttributeNumArgs(*this, attr, ReqArgs)) { attr.setInvalid(); @@ -3839,6 +3844,7 @@ bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC, CC = TI.getDefaultCallingConv(MT); } + attr.setProcessingCache((unsigned) CC); return false; } @@ -4033,31 +4039,45 @@ static bool isValidSubjectOfCFAttribute(Sema &S, QualType type) { } static void handleNSConsumedAttr(Sema &S, Decl *D, const AttributeList &Attr) { + S.AddNSConsumedAttr(Attr.getRange(), D, Attr.getAttributeSpellingListIndex(), + Attr.getKind() == AttributeList::AT_NSConsumed, + /*template instantiation*/ false); +} + +void Sema::AddNSConsumedAttr(SourceRange attrRange, Decl *D, + unsigned spellingIndex, bool isNSConsumed, + bool isTemplateInstantiation) { ParmVarDecl *param = cast(D); - bool typeOK, cf; + bool typeOK; - if (Attr.getKind() == AttributeList::AT_NSConsumed) { - typeOK = isValidSubjectOfNSAttribute(S, param->getType()); - cf = false; + if (isNSConsumed) { + typeOK = isValidSubjectOfNSAttribute(*this, param->getType()); } else { - typeOK = isValidSubjectOfCFAttribute(S, param->getType()); - cf = true; + typeOK = isValidSubjectOfCFAttribute(*this, param->getType()); } if (!typeOK) { - S.Diag(D->getLocStart(), diag::warn_ns_attribute_wrong_parameter_type) - << Attr.getRange() << Attr.getName() << cf; - return; - } - - if (cf) - param->addAttr(::new (S.Context) - CFConsumedAttr(Attr.getRange(), S.Context, - Attr.getAttributeSpellingListIndex())); + // These attributes are normally just advisory, but in ARC, ns_consumed + // is significant. Allow non-dependent code to contain inappropriate + // attributes even in ARC, but require template instantiations to be + // set up correctly. + Diag(D->getLocStart(), + (isTemplateInstantiation && isNSConsumed && + getLangOpts().ObjCAutoRefCount + ? diag::err_ns_attribute_wrong_parameter_type + : diag::warn_ns_attribute_wrong_parameter_type)) + << attrRange + << (isNSConsumed ? "ns_consumed" : "cf_consumed") + << (isNSConsumed ? /*objc pointers*/ 0 : /*cf pointers*/ 1); + return; + } + + if (isNSConsumed) + param->addAttr(::new (Context) + NSConsumedAttr(attrRange, Context, spellingIndex)); else - param->addAttr(::new (S.Context) - NSConsumedAttr(Attr.getRange(), S.Context, - Attr.getAttributeSpellingListIndex())); + param->addAttr(::new (Context) + CFConsumedAttr(attrRange, Context, spellingIndex)); } static void handleNSReturnsRetainedAttr(Sema &S, Decl *D, diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 59c636ce9b2..25473be796c 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -273,6 +273,14 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs, } } + if (isa(TmplAttr) || isa(TmplAttr)) { + AddNSConsumedAttr(TmplAttr->getRange(), New, + TmplAttr->getSpellingListIndex(), + isa(TmplAttr), + /*template instantiation*/ true); + continue; + } + assert(!TmplAttr->isPackExpansion()); if (TmplAttr->isLateParsed() && LateAttrs) { // Late parsed attributes must be instantiated and attached after the diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index e4b98feeed8..cf994a11a05 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -100,9 +100,8 @@ static void diagnoseBadTypeAttribute(Sema &S, const AttributeList &attr, case AttributeList::AT_ObjCGC: \ case AttributeList::AT_ObjCOwnership -// Function type attributes. -#define FUNCTION_TYPE_ATTRS_CASELIST \ - case AttributeList::AT_NoReturn: \ +// Calling convention attributes. +#define CALLING_CONV_ATTRS_CASELIST \ case AttributeList::AT_CDecl: \ case AttributeList::AT_FastCall: \ case AttributeList::AT_StdCall: \ @@ -111,12 +110,17 @@ static void diagnoseBadTypeAttribute(Sema &S, const AttributeList &attr, case AttributeList::AT_VectorCall: \ case AttributeList::AT_MSABI: \ case AttributeList::AT_SysVABI: \ - case AttributeList::AT_Regparm: \ case AttributeList::AT_Pcs: \ case AttributeList::AT_IntelOclBicc: \ case AttributeList::AT_PreserveMost: \ case AttributeList::AT_PreserveAll +// Function type attributes. +#define FUNCTION_TYPE_ATTRS_CASELIST \ + case AttributeList::AT_NoReturn: \ + case AttributeList::AT_Regparm: \ + CALLING_CONV_ATTRS_CASELIST + // Microsoft-specific type qualifiers. #define MS_TYPE_ATTRS_CASELIST \ case AttributeList::AT_Ptr32: \ @@ -2942,6 +2946,26 @@ getCCForDeclaratorChunk(Sema &S, Declarator &D, unsigned ChunkIndex) { assert(D.getTypeObject(ChunkIndex).Kind == DeclaratorChunk::Function); + // Check for an explicit CC attribute. + for (auto Attr = FTI.AttrList; Attr; Attr = Attr->getNext()) { + switch (Attr->getKind()) { + CALLING_CONV_ATTRS_CASELIST: { + // Ignore attributes that don't validate or can't apply to the + // function type. We'll diagnose the failure to apply them in + // handleFunctionTypeAttr. + CallingConv CC; + if (!S.CheckCallingConvAttr(*Attr, CC) && + (!FTI.isVariadic || supportsVariadicCall(CC))) { + return CC; + } + break; + } + + default: + break; + } + } + bool IsCXXInstanceMethod = false; if (S.getLangOpts().CPlusPlus) { @@ -5988,9 +6012,14 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state, // Modify the CC from the wrapped function type, wrap it all back, and then // wrap the whole thing in an AttributedType as written. The modified type // might have a different CC if we ignored the attribute. - FunctionType::ExtInfo EI = unwrapped.get()->getExtInfo().withCallingConv(CC); - QualType Equivalent = + QualType Equivalent; + if (CCOld == CC) { + Equivalent = type; + } else { + auto EI = unwrapped.get()->getExtInfo().withCallingConv(CC); + Equivalent = unwrapped.wrap(S, S.Context.adjustFunctionType(unwrapped.get(), EI)); + } type = S.Context.getAttributedType(CCAttrKind, type, Equivalent); return true; } diff --git a/test/SemaCXX/cxx11-gnu-attrs.cpp b/test/SemaCXX/cxx11-gnu-attrs.cpp index d20617815e6..231be727714 100644 --- a/test/SemaCXX/cxx11-gnu-attrs.cpp +++ b/test/SemaCXX/cxx11-gnu-attrs.cpp @@ -19,6 +19,7 @@ int *[[gnu::unused]] attr_on_ptr; [[gnu::fastcall]] void pr17424_4() [[gnu::stdcall]]; // expected-warning@-1 {{calling convention 'fastcall' ignored for this target}} // expected-warning@-2 {{attribute 'stdcall' ignored, because it cannot be applied to a type}} +// expected-warning@-3 {{calling convention 'stdcall' ignored for this target}} void pr17424_5 [[gnu::fastcall]](); // expected-warning@-1 {{calling convention 'fastcall' ignored for this target}} diff --git a/test/SemaObjCXX/arc-nsconsumed-errors.mm b/test/SemaObjCXX/arc-nsconsumed-errors.mm index c78d8a5f4ad..638a1ebd2ad 100644 --- a/test/SemaObjCXX/arc-nsconsumed-errors.mm +++ b/test/SemaObjCXX/arc-nsconsumed-errors.mm @@ -29,9 +29,9 @@ releaser_t r2 = releaser; // no-warning template -void templateFunction(T) { } // expected-note {{candidate template ignored: could not match 'void (__strong id)' against 'void (id)'}} \ +void templateFunction(T) { } // expected-note {{candidate template ignored: could not match 'void (__strong id)' against 'void (__attribute__((ns_consumed)) id)'}} \ // expected-note {{candidate template ignored: failed template argument deduction}} -releaser_t r3 = templateFunction; // expected-error {{address of overloaded function 'templateFunction' does not match required type 'void (id)'}} +releaser_t r3 = templateFunction; // expected-error {{address of overloaded function 'templateFunction' does not match required type 'void (__attribute__((ns_consumed)) id)'}} template void templateReleaser(__attribute__((ns_consumed)) T) { } // expected-note 2{{candidate template ignored: failed template argument deduction}} diff --git a/test/SemaObjCXX/arc-templates.mm b/test/SemaObjCXX/arc-templates.mm index 81425985e62..97854dff8c1 100644 --- a/test/SemaObjCXX/arc-templates.mm +++ b/test/SemaObjCXX/arc-templates.mm @@ -442,3 +442,11 @@ void test2() { take_no(produces<4>::fn); // expected-error {{no matching function}} } } + +namespace instantiate_consumed { + template void take(CONSUMED T t) {} // expected-note {{candidate template ignored: substitution failure [with T = int]: ns_consumed attribute only applies to Objective-C object parameters}} + void test() { + take((id) 0); + take((int) 0); // expected-error {{no matching function for call to 'take'}} + } +} From 08f7e860962b256131511905f9c5d8ec408ab552 Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 4 Apr 2016 11:59:39 -0700 Subject: [PATCH 422/742] Manglings for Roman's new calling conventions. --- lib/AST/ItaniumMangle.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index 269278b96dd..c5b6a358939 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -2151,6 +2151,11 @@ StringRef CXXNameMangler::getCallingConvQualifierName(CallingConv CC) { case CC_C: return ""; + case CC_PreserveMost: + return "perservemost"; + case CC_PreserveAll: + return "perserveall"; + case CC_X86StdCall: case CC_X86FastCall: case CC_X86ThisCall: From 11cd9f83fb9086f6fce6286e840012eac98e578a Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 3 Mar 2016 06:39:32 +0000 Subject: [PATCH 423/742] Semantic analysis for the swiftcall calling convention. I've tried to keep the infrastructure behind parameter ABI treatments fairly general. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262587 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang-c/Index.h | 1 + include/clang/AST/Attr.h | 30 +++++ include/clang/AST/Type.h | 21 ++- include/clang/Basic/Attr.td | 26 ++++ include/clang/Basic/AttrDocs.td | 143 +++++++++++++++++++++ include/clang/Basic/DiagnosticSemaKinds.td | 14 ++ include/clang/Basic/Specifiers.h | 24 ++++ include/clang/Sema/Sema.h | 3 + lib/AST/ItaniumMangle.cpp | 17 ++- lib/AST/Type.cpp | 3 + lib/AST/TypePrinter.cpp | 21 +++ lib/Basic/Targets.cpp | 33 +++-- lib/Sema/SemaDeclAttr.cpp | 106 +++++++++++++++ lib/Sema/SemaTemplateInstantiateDecl.cpp | 6 + lib/Sema/SemaType.cpp | 89 ++++++++++++- test/Sema/attr-swiftcall.c | 30 +++++ test/SemaCXX/attr-swiftcall.cpp | 37 ++++++ tools/libclang/CXType.cpp | 1 + utils/TableGen/ClangAttrEmitter.cpp | 3 +- 19 files changed, 595 insertions(+), 13 deletions(-) create mode 100644 test/Sema/attr-swiftcall.c create mode 100644 test/SemaCXX/attr-swiftcall.cpp diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h index cbb5052ecfc..28ec30ccba5 100644 --- a/include/clang-c/Index.h +++ b/include/clang-c/Index.h @@ -2943,6 +2943,7 @@ enum CXCallingConv { CXCallingConv_X86_64Win64 = 10, CXCallingConv_X86_64SysV = 11, CXCallingConv_X86VectorCall = 12, + CXCallingConv_Swift = 13, CXCallingConv_PreserveMost = 14, CXCallingConv_PreserveAll = 15, diff --git a/include/clang/AST/Attr.h b/include/clang/AST/Attr.h index 698909f0335..4d864edf6a5 100644 --- a/include/clang/AST/Attr.h +++ b/include/clang/AST/Attr.h @@ -149,6 +149,36 @@ class InheritableParamAttr : public InheritableAttr { } }; +/// A parameter attribute which changes the argument-passing ABI rule +/// for the parameter. +class ParameterABIAttr : public InheritableParamAttr { +protected: + ParameterABIAttr(attr::Kind AK, SourceRange R, + unsigned SpellingListIndex, bool IsLateParsed, + bool DuplicatesAllowed) + : InheritableParamAttr(AK, R, SpellingListIndex, IsLateParsed, + DuplicatesAllowed) {} + +public: + ParameterABI getABI() const { + switch (getKind()) { + case attr::SwiftContext: + return ParameterABI::SwiftContext; + case attr::SwiftErrorResult: + return ParameterABI::SwiftErrorResult; + case attr::SwiftIndirectResult: + return ParameterABI::SwiftIndirectResult; + default: + llvm_unreachable("bad parameter ABI attribute kind"); + } + } + + static bool classof(const Attr *A) { + return A->getKind() >= attr::FirstParameterABIAttr && + A->getKind() <= attr::LastParameterABIAttr; + } +}; + #include "clang/AST/Attrs.inc" inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB, diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 4e26236024b..2e02ad1ea0f 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -3061,12 +3061,23 @@ class FunctionProtoType : public FunctionType, public llvm::FoldingSetNode { /// not produce the latter. class ExtParameterInfo { enum { - IsConsumed = 0x01, + ABIMask = 0x0F, + IsConsumed = 0x10 }; unsigned char Data; public: ExtParameterInfo() : Data(0) {} + /// Return the ABI treatment of this parameter. + ParameterABI getABI() const { + return ParameterABI(Data & ABIMask); + } + ExtParameterInfo withABI(ParameterABI kind) const { + ExtParameterInfo copy = *this; + copy.Data = (copy.Data & ~ABIMask) | unsigned(kind); + return copy; + } + /// Is this parameter considered "consumed" by Objective-C ARC? /// Consumed parameters must have retainable object type. bool isConsumed() const { @@ -3392,6 +3403,13 @@ class FunctionProtoType : public FunctionType, public llvm::FoldingSetNode { return ExtParameterInfo(); } + ParameterABI getParameterABI(unsigned I) const { + assert(I < getNumParams() && "parameter index out of range"); + if (hasExtParameterInfos()) + return getExtParameterInfosBuffer()[I].getABI(); + return ParameterABI::Ordinary; + } + bool isParamConsumed(unsigned I) const { assert(I < getNumParams() && "parameter index out of range"); if (hasExtParameterInfos()) @@ -3717,6 +3735,7 @@ class AttributedType : public Type, public llvm::FoldingSetNode { attr_stdcall, attr_thiscall, attr_pascal, + attr_swiftcall, attr_vectorcall, attr_inteloclbicc, attr_ms_abi, diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index b75035a6e9b..7adfdc4c107 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -341,6 +341,11 @@ class TargetSpecificAttr { /// redeclarations, even when it's written on a parameter. class InheritableParamAttr : InheritableAttr; +/// An attribute which changes the ABI rules for a specific parameter. +class ParameterABIAttr : InheritableParamAttr { + let Subjects = SubjectList<[ParmVar]>; +} + /// An ignored attribute, which we parse but discard with no checking. class IgnoredAttr : Attr { let Ignored = 1; @@ -1402,6 +1407,27 @@ def StdCall : InheritableAttr { let Documentation = [StdCallDocs]; } +def SwiftCall : InheritableAttr { + let Spellings = [GCC<"swiftcall">]; +// let Subjects = SubjectList<[Function]>; + let Documentation = [SwiftCallDocs]; +} + +def SwiftContext : ParameterABIAttr { + let Spellings = [GCC<"swift_context">]; + let Documentation = [SwiftContextDocs]; +} + +def SwiftErrorResult : ParameterABIAttr { + let Spellings = [GCC<"swift_error_result">]; + let Documentation = [SwiftErrorResultDocs]; +} + +def SwiftIndirectResult : ParameterABIAttr { + let Spellings = [GCC<"swift_indirect_result">]; + let Documentation = [SwiftIndirectResultDocs]; +} + def SysVABI : InheritableAttr { let Spellings = [GCC<"sysv_abi">]; // let Subjects = [Function, ObjCMethod]; diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td index d0562615f51..24d212967a8 100644 --- a/include/clang/Basic/AttrDocs.td +++ b/include/clang/Basic/AttrDocs.td @@ -2028,3 +2028,146 @@ experimental at this time. }]; } +def SwiftCallDocs : Documentation { + let Category = DocCatVariable; + let Content = [{ +The ``swiftcall`` attribute indicates that a function should be called +using the Swift calling convention for a function or function pointer. + +The lowering for the Swift calling convention, as described by the Swift +ABI documentation, occurs in multiple phases. The first, "high-level" +phase breaks down the formal parameters and results into innately direct +and indirect components, adds implicit paraameters for the generic +signature, and assigns the context and error ABI treatments to parameters +where applicable. The second phase breaks down the direct parameters +and results from the first phase and assigns them to registers or the +stack. The ``swiftcall`` convention only handles this second phase of +lowering; the C function type must accurately reflect the results +of the first phase, as follows: + +- Results classified as indirect by high-level lowering should be + represented as parameters with the ``swift_indirect_result`` attribute. + +- Results classified as direct by high-level lowering should be represented + as follows: + + - First, remove any empty direct results. + + - If there are no direct results, the C result type should be ``void``. + + - If there is one direct result, the C result type should be a type with + the exact layout of that result type. + + - If there are a multiple direct results, the C result type should be + a struct type with the exact layout of a tuple of those results. + +- Parameters classified as indirect by high-level lowering should be + represented as parameters of pointer type. + +- Parameters classified as direct by high-level lowering should be + omitted if they are empty types; otherwise, they should be represented + as a parameter type with a layout exactly matching the layout of the + Swift parameter type. + +- The context parameter, if present, should be represented as a trailing + parameter with the ``swift_context`` attribute. + +- The error result parameter, if present, should be represented as a + trailing parameter (always following a context parameter) with the + ``swift_error_result`` attribute. + +``swiftcall`` does not support variadic arguments or unprototyped functions. + +The parameter ABI treatment attributes are aspects of the function type. +A function type which which applies an ABI treatment attribute to a +parameter is a different type from an otherwise-identical function type +that does not. A single parameter may not have multiple ABI treatment +attributes. + +Support for this feature is target-dependent, although it should be +supported on every target that Swift supports. Query for this support +with ``__has_attribute(swiftcall)``. This implies support for the +``swift_context``, ``swift_error_result``, and ``swift_indirect_result`` +attributes. + }]; +} + +def SwiftContextDocs : Documentation { + let Category = DocCatVariable; + let Content = [{ +The ``swift_context`` attribute marks a parameter of a ``swiftcall`` +function as having the special context-parameter ABI treatment. + +This treatment generally passes the context value in a special register +which is normally callee-preserved. + +A ``swift_context`` parameter must either be the last parameter or must be +followed by a ``swift_error_result`` parameter (which itself must always be +the last parameter). + +A context parameter must have pointer or reference type. + }]; +} + +def SwiftErrorResultDocs : Documentation { + let Category = DocCatVariable; + let Content = [{ +The ``swift_error_result`` attribute marks a parameter of a ``swiftcall`` +function as having the special error-result ABI treatment. + +This treatment generally passes the underlying error value in and out of +the function through a special register which is normally callee-preserved. +This is modeled in C by pretending that the register is addressable memory: + +- The caller appears to pass the address of a variable of pointer type. + The current value of this variable is copied into the register before + the call; if the call returns normally, the value is copied back into the + variable. + +- The callee appears to receive the address of a variable. This address + is actually a hidden location in its own stack, initialized with the + value of the register upon entry. When the function returns normally, + the value in that hidden location is written back to the register. + +A ``swift_error_result`` parameter must be the last parameter, and it must be +preceded by a ``swift_context`` parameter. + +A ``swift_error_result`` parameter must have type ``T**`` or ``T*&`` for some +type T. Note that no qualifiers are permitted on the intermediate level. + +It is undefined behavior if the caller does not pass a pointer or +reference to a valid object. + +The standard convention is that the error value itself (that is, the +value stored in the apparent argument) will be null upon function entry, +but this is not enforced by the ABI. + }]; +} + +def SwiftIndirectResultDocs : Documentation { + let Category = DocCatVariable; + let Content = [{ +The ``swift_indirect_result`` attribute marks a parameter of a ``swiftcall`` +function as having the special indirect-result ABI treatmenet. + +This treatment gives the parameter the target's normal indirect-result +ABI treatment, which may involve passing it differently from an ordinary +parameter. However, only the first indirect result will receive this +treatment. Furthermore, low-level lowering may decide that a direct result +must be returned indirectly; if so, this will take priority over the +``swift_indirect_result`` parameters. + +A ``swift_indirect_result`` parameter must either be the first parameter or +follow another ``swift_indirect_result`` parameter. + +A ``swift_indirect_result`` parameter must have type ``T*`` or ``T&`` for +some object type ``T``. If ``T`` is a complete type at the point of +definition of a function, it is undefined behavior if the argument +value does not point to storage of adequate size and alignment for a +value of type ``T``. + +Making indirect results explicit in the signature allows C functions to +directly construct objects into them without relying on language +optimizations like C++'s named return value optimization (NRVO). + }]; +} diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index bfa75d5e446..765e6368f4a 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -2267,6 +2267,20 @@ def warn_objc_collection_literal_element : Warning< "object of type %0 is not compatible with " "%select{array element type|dictionary key type|dictionary value type}1 %2">, InGroup; +def err_swift_param_attr_not_swiftcall : Error< + "'%0' parameter can only be used with swiftcall calling convention">; +def err_swift_indirect_result_not_first : Error< + "'swift_indirect_result' parameters must be first parameters of function">; +def err_swift_context_not_before_swift_error_result : Error< + "'swift_context' parameter can only be followed by 'swift_error_result' " + "parameter">; +def err_swift_error_result_not_last : Error< + "'swift_error_result' parameter must be last parameter of function">; +def err_swift_error_result_not_after_swift_context : Error< + "'swift_error_result' parameter must follow 'swift_context' parameter">; +def err_swift_abi_parameter_wrong_type : Error< + "'%0' parameter must have pointer%select{| to unqualified pointer}1 type; " + "type here is %2">; def err_attribute_argument_is_zero : Error< "%0 attribute must be greater than 0">; diff --git a/include/clang/Basic/Specifiers.h b/include/clang/Basic/Specifiers.h index c9529eb223f..8857e0df565 100644 --- a/include/clang/Basic/Specifiers.h +++ b/include/clang/Basic/Specifiers.h @@ -239,6 +239,7 @@ namespace clang { CC_IntelOclBicc, // __attribute__((intel_ocl_bicc)) CC_SpirFunction, // default for OpenCL functions on SPIR target CC_SpirKernel, // inferred for OpenCL kernels on SPIR target + CC_Swift, // __attribute__((swiftcall)) CC_PreserveMost, // __attribute__((preserve_most)) CC_PreserveAll, // __attribute__((preserve_all)) }; @@ -254,6 +255,7 @@ namespace clang { case CC_X86VectorCall: case CC_SpirFunction: case CC_SpirKernel: + case CC_Swift: return false; default: return true; @@ -285,6 +287,28 @@ namespace clang { /// Retrieve the spelling of the given nullability kind. llvm::StringRef getNullabilitySpelling(NullabilityKind kind, bool isContextSensitive = false); + + /// \brief Kinds of parameter ABI. + enum class ParameterABI { + /// This parameter uses ordinary ABI rules for its type. + Ordinary, + + /// This parameter (which must have pointer type) is a Swift + /// indirect result parameter. + SwiftIndirectResult, + + /// This parameter (which must have pointer-to-pointer type) uses + /// the special Swift error-result ABI treatment. There can be at + /// most one parameter on a given function that uses this treatment. + SwiftErrorResult, + + /// This parameter (which must have pointer type) uses the special + /// Swift context-pointer ABI treatment. There can be at + /// most one parameter on a given function that uses this treatment. + SwiftContext + }; + + llvm::StringRef getParameterABISpelling(ParameterABI kind); } // end namespace clang #endif // LLVM_CLANG_BASIC_SPECIFIERS_H diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index e6b66b9d1ad..ba7e510ae41 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -7797,6 +7797,9 @@ class Sema { void AddLaunchBoundsAttr(SourceRange AttrRange, Decl *D, Expr *MaxThreads, Expr *MinBlocks, unsigned SpellingListIndex); + void AddParameterABIAttr(SourceRange AttrRange, Decl *D, + ParameterABI ABI, unsigned SpellingListIndex); + void AddNSConsumedAttr(SourceRange AttrRange, Decl *D, unsigned SpellingListIndex, bool isNSConsumed, bool isTemplateInstantiation); diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index c5b6a358939..b8022a4b56d 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -2170,6 +2170,9 @@ StringRef CXXNameMangler::getCallingConvQualifierName(CallingConv CC) { case CC_SpirKernel: // FIXME: we should be mangling all of the above. return ""; + + case CC_Swift: + return "swiftcall"; } llvm_unreachable("bad calling convention"); } @@ -2198,8 +2201,20 @@ CXXNameMangler::mangleExtParameterInfo(FunctionProtoType::ExtParameterInfo PI) { // Note that these are *not* substitution candidates. Demanglers might // have trouble with this if the parameter type is fully substituted. + switch (PI.getABI()) { + case ParameterABI::Ordinary: + break; + + // All of these start with "swift", so they come before "ns_consumed". + case ParameterABI::SwiftContext: + case ParameterABI::SwiftErrorResult: + case ParameterABI::SwiftIndirectResult: + mangleVendorQualifier(getParameterABISpelling(PI.getABI())); + break; + } + if (PI.isConsumed()) - Out << "U11ns_consumed"; + mangleVendorQualifier("ns_consumed"); } // ::= diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index a1b457b51a2..74aa323dbc8 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -2655,6 +2655,7 @@ StringRef FunctionType::getNameForCallConv(CallingConv CC) { case CC_IntelOclBicc: return "intel_ocl_bicc"; case CC_SpirFunction: return "spir_function"; case CC_SpirKernel: return "spir_kernel"; + case CC_Swift: return "swiftcall"; case CC_PreserveMost: return "preserve_most"; case CC_PreserveAll: return "preserve_all"; } @@ -2997,6 +2998,7 @@ bool AttributedType::isQualifier() const { case AttributedType::attr_stdcall: case AttributedType::attr_thiscall: case AttributedType::attr_pascal: + case AttributedType::attr_swiftcall: case AttributedType::attr_vectorcall: case AttributedType::attr_inteloclbicc: case AttributedType::attr_preserve_most: @@ -3052,6 +3054,7 @@ bool AttributedType::isCallingConv() const { case attr_fastcall: case attr_stdcall: case attr_thiscall: + case attr_swiftcall: case attr_vectorcall: case attr_pascal: case attr_ms_abi: diff --git a/lib/AST/TypePrinter.cpp b/lib/AST/TypePrinter.cpp index 3b96cc5f892..7c519fc6381 100644 --- a/lib/AST/TypePrinter.cpp +++ b/lib/AST/TypePrinter.cpp @@ -629,6 +629,20 @@ void TypePrinter::printFunctionProtoBefore(const FunctionProtoType *T, } } +llvm::StringRef clang::getParameterABISpelling(ParameterABI ABI) { + switch (ABI) { + case ParameterABI::Ordinary: + llvm_unreachable("asking for spelling of ordinary parameter ABI"); + case ParameterABI::SwiftContext: + return "swift_context"; + case ParameterABI::SwiftErrorResult: + return "swift_error_result"; + case ParameterABI::SwiftIndirectResult: + return "swift_indirect_result"; + } + llvm_unreachable("bad parameter ABI kind"); +} + void TypePrinter::printFunctionProtoAfter(const FunctionProtoType *T, raw_ostream &OS) { // If needed for precedence reasons, wrap the inner part in grouping parens. @@ -644,6 +658,9 @@ void TypePrinter::printFunctionProtoAfter(const FunctionProtoType *T, auto EPI = T->getExtParameterInfo(i); if (EPI.isConsumed()) OS << "__attribute__((ns_consumed)) "; + auto ABI = EPI.getABI(); + if (ABI != ParameterABI::Ordinary) + OS << "__attribute__((" << getParameterABISpelling(ABI) << ")) "; print(T->getParamType(i), OS, StringRef()); } @@ -707,6 +724,9 @@ void TypePrinter::printFunctionProtoAfter(const FunctionProtoType *T, case CC_SpirKernel: // Do nothing. These CCs are not available as attributes. break; + case CC_Swift: + OS << " __attribute__((swiftcall))"; + break; case CC_PreserveMost: OS << " __attribute__((preserve_most))"; break; @@ -1315,6 +1335,7 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, case AttributedType::attr_fastcall: OS << "fastcall"; break; case AttributedType::attr_stdcall: OS << "stdcall"; break; case AttributedType::attr_thiscall: OS << "thiscall"; break; + case AttributedType::attr_swiftcall: OS << "swiftcall"; break; case AttributedType::attr_vectorcall: OS << "vectorcall"; break; case AttributedType::attr_pascal: OS << "pascal"; break; case AttributedType::attr_ms_abi: OS << "ms_abi"; break; diff --git a/lib/Basic/Targets.cpp b/lib/Basic/Targets.cpp index af2a8c9c590..8072e6973db 100644 --- a/lib/Basic/Targets.cpp +++ b/lib/Basic/Targets.cpp @@ -2521,14 +2521,20 @@ class X86TargetInfo : public TargetInfo { bool setFPMath(StringRef Name) override; CallingConvCheckResult checkCallingConvention(CallingConv CC) const override { - // We accept all non-ARM calling conventions - return (CC == CC_X86ThisCall || - CC == CC_X86FastCall || - CC == CC_X86StdCall || - CC == CC_X86VectorCall || - CC == CC_C || - CC == CC_X86Pascal || - CC == CC_IntelOclBicc) ? CCCR_OK : CCCR_Warning; + // Most of the non-ARM calling conventions are i386 conventions. + switch (CC) { + case CC_X86ThisCall: + case CC_X86FastCall: + case CC_X86StdCall: + case CC_X86VectorCall: + case CC_C: + case CC_Swift: + case CC_X86Pascal: + case CC_IntelOclBicc: + return CCCR_OK; + default: + return CCCR_Warning; + } } CallingConv getDefaultCallingConv(CallingConvMethodType MT) const override { @@ -4019,6 +4025,7 @@ class X86_64TargetInfo : public X86TargetInfo { CallingConvCheckResult checkCallingConvention(CallingConv CC) const override { switch (CC) { case CC_C: + case CC_Swift: case CC_X86VectorCall: case CC_IntelOclBicc: case CC_X86_64Win64: @@ -5003,7 +5010,14 @@ class ARMTargetInfo : public TargetInfo { } CallingConvCheckResult checkCallingConvention(CallingConv CC) const override { - return (CC == CC_AAPCS || CC == CC_AAPCS_VFP) ? CCCR_OK : CCCR_Warning; + switch (CC) { + case CC_AAPCS: + case CC_AAPCS_VFP: + case CC_Swift: + return CCCR_OK; + default: + return CCCR_Warning; + } } int getEHDataRegisterNumber(unsigned RegNo) const override { @@ -5458,6 +5472,7 @@ class AArch64TargetInfo : public TargetInfo { CallingConvCheckResult checkCallingConvention(CallingConv CC) const override { switch (CC) { case CC_C: + case CC_Swift: case CC_PreserveMost: case CC_PreserveAll: return CCCR_OK; diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 5d74e64d662..41d68edcc95 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -3722,6 +3722,11 @@ static void handleCallConvAttr(Sema &S, Decl *D, const AttributeList &Attr) { PascalAttr(Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); return; + case AttributeList::AT_SwiftCall: + D->addAttr(::new (S.Context) + SwiftCallAttr(Attr.getRange(), S.Context, + Attr.getAttributeSpellingListIndex())); + return; case AttributeList::AT_VectorCall: D->addAttr(::new (S.Context) VectorCallAttr(Attr.getRange(), S.Context, @@ -3796,6 +3801,7 @@ bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC, case AttributeList::AT_StdCall: CC = CC_X86StdCall; break; case AttributeList::AT_ThisCall: CC = CC_X86ThisCall; break; case AttributeList::AT_Pascal: CC = CC_X86Pascal; break; + case AttributeList::AT_SwiftCall: CC = CC_Swift; break; case AttributeList::AT_VectorCall: CC = CC_X86VectorCall; break; case AttributeList::AT_MSABI: CC = Context.getTargetInfo().getTriple().isOSWindows() ? CC_C : @@ -3848,6 +3854,96 @@ bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC, return false; } +/// Pointer-like types in the default address space. +static bool isValidSwiftContextType(QualType type) { + if (!type->hasPointerRepresentation()) + return type->isDependentType(); + return type->getPointeeType().getAddressSpace() == 0; +} + +/// Pointers and references in the default address space. +static bool isValidSwiftIndirectResultType(QualType type) { + if (auto ptrType = type->getAs()) { + type = ptrType->getPointeeType(); + } else if (auto refType = type->getAs()) { + type = refType->getPointeeType(); + } else { + return type->isDependentType(); + } + return type.getAddressSpace() == 0; +} + +/// Pointers and references to pointers in the default address space. +static bool isValidSwiftErrorResultType(QualType type) { + if (auto ptrType = type->getAs()) { + type = ptrType->getPointeeType(); + } else if (auto refType = type->getAs()) { + type = refType->getPointeeType(); + } else { + return type->isDependentType(); + } + if (!type.getQualifiers().empty()) + return false; + return isValidSwiftContextType(type); +} + +static void handleParameterABIAttr(Sema &S, Decl *D, const AttributeList &attr, + ParameterABI abi) { + S.AddParameterABIAttr(attr.getRange(), D, abi, + attr.getAttributeSpellingListIndex()); +} + +void Sema::AddParameterABIAttr(SourceRange range, Decl *D, ParameterABI abi, + unsigned spellingIndex) { + + QualType type = cast(D)->getType(); + + if (auto existingAttr = D->getAttr()) { + if (existingAttr->getABI() != abi) { + Diag(range.getBegin(), diag::err_attributes_are_not_compatible) + << getParameterABISpelling(abi) << existingAttr; + Diag(existingAttr->getLocation(), diag::note_conflicting_attribute); + return; + } + } + + switch (abi) { + case ParameterABI::Ordinary: + llvm_unreachable("explicit attribute for ordinary parameter ABI?"); + + case ParameterABI::SwiftContext: + if (!isValidSwiftContextType(type)) { + Diag(range.getBegin(), diag::err_swift_abi_parameter_wrong_type) + << getParameterABISpelling(abi) + << /*pointer to pointer */ 0 << type; + } + D->addAttr(::new (Context) + SwiftContextAttr(range, Context, spellingIndex)); + return; + + case ParameterABI::SwiftErrorResult: + if (!isValidSwiftErrorResultType(type)) { + Diag(range.getBegin(), diag::err_swift_abi_parameter_wrong_type) + << getParameterABISpelling(abi) + << /*pointer to pointer */ 1 << type; + } + D->addAttr(::new (Context) + SwiftErrorResultAttr(range, Context, spellingIndex)); + return; + + case ParameterABI::SwiftIndirectResult: + if (!isValidSwiftIndirectResultType(type)) { + Diag(range.getBegin(), diag::err_swift_abi_parameter_wrong_type) + << getParameterABISpelling(abi) + << /*pointer*/ 0 << type; + } + D->addAttr(::new (Context) + SwiftIndirectResultAttr(range, Context, spellingIndex)); + return; + } + llvm_unreachable("bad parameter ABI attribute"); +} + /// Checks a regparm attribute, returning true if it is ill-formed and /// otherwise setting numParams to the appropriate value. bool Sema::CheckRegparmAttr(const AttributeList &Attr, unsigned &numParams) { @@ -5810,6 +5906,7 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_FastCall: case AttributeList::AT_ThisCall: case AttributeList::AT_Pascal: + case AttributeList::AT_SwiftCall: case AttributeList::AT_VectorCall: case AttributeList::AT_MSABI: case AttributeList::AT_SysVABI: @@ -5825,6 +5922,15 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_OpenCLImageAccess: handleSimpleAttribute(S, D, Attr); break; + case AttributeList::AT_SwiftContext: + handleParameterABIAttr(S, D, Attr, ParameterABI::SwiftContext); + break; + case AttributeList::AT_SwiftErrorResult: + handleParameterABIAttr(S, D, Attr, ParameterABI::SwiftErrorResult); + break; + case AttributeList::AT_SwiftIndirectResult: + handleParameterABIAttr(S, D, Attr, ParameterABI::SwiftIndirectResult); + break; case AttributeList::AT_InternalLinkage: handleInternalLinkageAttr(S, D, Attr); break; diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 25473be796c..07fbbf78172 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -273,6 +273,12 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs, } } + if (auto ABIAttr = dyn_cast(TmplAttr)) { + AddParameterABIAttr(ABIAttr->getRange(), New, ABIAttr->getABI(), + ABIAttr->getSpellingListIndex()); + continue; + } + if (isa(TmplAttr) || isa(TmplAttr)) { AddNSConsumedAttr(TmplAttr->getRange(), New, TmplAttr->getSpellingListIndex(), diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index cf994a11a05..0da3bcc230f 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -107,6 +107,7 @@ static void diagnoseBadTypeAttribute(Sema &S, const AttributeList &attr, case AttributeList::AT_StdCall: \ case AttributeList::AT_ThisCall: \ case AttributeList::AT_Pascal: \ + case AttributeList::AT_SwiftCall: \ case AttributeList::AT_VectorCall: \ case AttributeList::AT_MSABI: \ case AttributeList::AT_SysVABI: \ @@ -2253,6 +2254,74 @@ bool Sema::CheckFunctionReturnType(QualType T, SourceLocation Loc) { return false; } +/// Check the extended parameter information. Most of the necessary +/// checking should occur when applying the parameter attribute; the +/// only other checks required are positional restrictions. +static void checkExtParameterInfos(Sema &S, ArrayRef paramTypes, + const FunctionProtoType::ExtProtoInfo &EPI, + llvm::function_ref getParamLoc) { + assert(EPI.ExtParameterInfos && "shouldn't get here without param infos"); + + bool hasCheckedSwiftCall = false; + auto checkForSwiftCC = [&](unsigned paramIndex) { + // Only do this once. + if (hasCheckedSwiftCall) return; + hasCheckedSwiftCall = true; + if (EPI.ExtInfo.getCC() == CC_Swift) return; + S.Diag(getParamLoc(paramIndex), diag::err_swift_param_attr_not_swiftcall) + << getParameterABISpelling(EPI.ExtParameterInfos[paramIndex].getABI()); + }; + + for (size_t paramIndex = 0, numParams = paramTypes.size(); + paramIndex != numParams; ++paramIndex) { + switch (EPI.ExtParameterInfos[paramIndex].getABI()) { + // Nothing interesting to check for orindary-ABI parameters. + case ParameterABI::Ordinary: + continue; + + // swift_indirect_result parameters must be a prefix of the function + // arguments. + case ParameterABI::SwiftIndirectResult: + checkForSwiftCC(paramIndex); + if (paramIndex != 0 && + EPI.ExtParameterInfos[paramIndex - 1].getABI() + != ParameterABI::SwiftIndirectResult) { + S.Diag(getParamLoc(paramIndex), + diag::err_swift_indirect_result_not_first); + } + continue; + + // swift_context parameters must be the last parameter except for + // a possible swift_error parameter. + case ParameterABI::SwiftContext: + checkForSwiftCC(paramIndex); + if (!(paramIndex == numParams - 1 || + (paramIndex == numParams - 2 && + EPI.ExtParameterInfos[numParams - 1].getABI() + == ParameterABI::SwiftErrorResult))) { + S.Diag(getParamLoc(paramIndex), + diag::err_swift_context_not_before_swift_error_result); + } + continue; + + // swift_error parameters must be the last parameter. + case ParameterABI::SwiftErrorResult: + checkForSwiftCC(paramIndex); + if (paramIndex != numParams - 1) { + S.Diag(getParamLoc(paramIndex), + diag::err_swift_error_result_not_last); + } else if (paramIndex == 0 || + EPI.ExtParameterInfos[paramIndex - 1].getABI() + != ParameterABI::SwiftContext) { + S.Diag(getParamLoc(paramIndex), + diag::err_swift_error_result_not_after_swift_context); + } + continue; + } + llvm_unreachable("bad ABI kind"); + } +} + QualType Sema::BuildFunctionType(QualType T, MutableArrayRef ParamTypes, SourceLocation Loc, DeclarationName Entity, @@ -2277,6 +2346,11 @@ QualType Sema::BuildFunctionType(QualType T, ParamTypes[Idx] = ParamType; } + if (EPI.ExtParameterInfos) { + checkExtParameterInfos(*this, ParamTypes, EPI, + [=](unsigned i) { return Loc; }); + } + if (Invalid) return QualType(); @@ -4062,11 +4136,20 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, HasAnyInterestingExtParameterInfos = true; } + if (auto attr = Param->getAttr()) { + ExtParameterInfos[i] = + ExtParameterInfos[i].withABI(attr->getABI()); + HasAnyInterestingExtParameterInfos = true; + } + ParamTys.push_back(ParamTy); } - if (HasAnyInterestingExtParameterInfos) + if (HasAnyInterestingExtParameterInfos) { EPI.ExtParameterInfos = ExtParameterInfos.data(); + checkExtParameterInfos(S, ParamTys, EPI, + [&](unsigned i) { return FTI.Params[i].Param->getLocation(); }); + } SmallVector Exceptions; SmallVector DynamicExceptions; @@ -4526,6 +4609,8 @@ static AttributeList::Kind getAttrListKind(AttributedType::Kind kind) { return AttributeList::AT_ThisCall; case AttributedType::attr_pascal: return AttributeList::AT_Pascal; + case AttributedType::attr_swiftcall: + return AttributeList::AT_SwiftCall; case AttributedType::attr_vectorcall: return AttributeList::AT_VectorCall; case AttributedType::attr_pcs: @@ -5855,6 +5940,8 @@ static AttributedType::Kind getCCTypeAttrKind(AttributeList &Attr) { return AttributedType::attr_thiscall; case AttributeList::AT_Pascal: return AttributedType::attr_pascal; + case AttributeList::AT_SwiftCall: + return AttributedType::attr_swiftcall; case AttributeList::AT_VectorCall: return AttributedType::attr_vectorcall; case AttributeList::AT_Pcs: { diff --git a/test/Sema/attr-swiftcall.c b/test/Sema/attr-swiftcall.c new file mode 100644 index 00000000000..3458167cf2e --- /dev/null +++ b/test/Sema/attr-swiftcall.c @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -verify %s + +#define SWIFTCALL __attribute__((swiftcall)) +#define INDIRECT_RESULT __attribute__((swift_indirect_result)) +#define ERROR_RESULT __attribute__((swift_error_result)) +#define CONTEXT __attribute__((swift_context)) + +int notAFunction SWIFTCALL; // expected-warning {{'swiftcall' only applies to function types; type here is 'int'}} +void variadic(int x, ...) SWIFTCALL; // expected-error {{variadic function cannot use swiftcall calling convention}} +void unprototyped() SWIFTCALL; // expected-error {{function with no prototype cannot use the swiftcall calling convention}} +void multiple_ccs(int x) SWIFTCALL __attribute__((vectorcall)); // expected-error {{vectorcall and swiftcall attributes are not compatible}} +void (*functionPointer)(void) SWIFTCALL; + +void indirect_result_nonswift(INDIRECT_RESULT void *out); // expected-error {{'swift_indirect_result' parameter can only be used with swiftcall calling convention}} +void indirect_result_bad_position(int first, INDIRECT_RESULT void *out) SWIFTCALL; // expected-error {{'swift_indirect_result' parameters must be first parameters of function}} +void indirect_result_bad_type(INDIRECT_RESULT int out) SWIFTCALL; // expected-error {{'swift_indirect_result' parameter must have pointer type; type here is 'int'}} +void indirect_result_single(INDIRECT_RESULT void *out) SWIFTCALL; +void indirect_result_multiple(INDIRECT_RESULT void *out1, INDIRECT_RESULT void *out2) SWIFTCALL; + +void error_result_nonswift(ERROR_RESULT void **error); // expected-error {{'swift_error_result' parameter can only be used with swiftcall calling convention}} expected-error{{'swift_error_result' parameter must follow 'swift_context' parameter}} +void error_result_bad_position(ERROR_RESULT void **error, int last) SWIFTCALL; // expected-error {{'swift_error_result' parameter must be last parameter of function}} +void error_result_bad_position2(int first, ERROR_RESULT void **error) SWIFTCALL; // expected-error {{'swift_error_result' parameter must follow 'swift_context' parameter}} +void error_result_bad_type(CONTEXT void *context, ERROR_RESULT int error) SWIFTCALL; // expected-error {{'swift_error_result' parameter must have pointer to unqualified pointer type; type here is 'int'}} +void error_result_bad_type2(CONTEXT void *context, ERROR_RESULT int *error) SWIFTCALL; // expected-error {{'swift_error_result' parameter must have pointer to unqualified pointer type; type here is 'int *'}} +void error_result_okay(int a, int b, CONTEXT void *context, ERROR_RESULT void **error) SWIFTCALL; + +void context_nonswift(CONTEXT void *context); // expected-error {{'swift_context' parameter can only be used with swiftcall calling convention}} +void context_bad_position(CONTEXT void *context, int x) SWIFTCALL; // expected-error {{'swift_context' parameter can only be followed by 'swift_error_result' parameter}} +void context_bad_type(CONTEXT int context) SWIFTCALL; // expected-error {{'swift_context' parameter must have pointer type; type here is 'int'}} +void context_okay(CONTEXT void *context) SWIFTCALL; diff --git a/test/SemaCXX/attr-swiftcall.cpp b/test/SemaCXX/attr-swiftcall.cpp new file mode 100644 index 00000000000..fd17aae1852 --- /dev/null +++ b/test/SemaCXX/attr-swiftcall.cpp @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -verify %s + +#define SWIFTCALL __attribute__((swiftcall)) +#define INDIRECT_RESULT __attribute__((swift_indirect_result)) +#define ERROR_RESULT __attribute__((swift_error_result)) +#define CONTEXT __attribute__((swift_context)) + +int notAFunction SWIFTCALL; // expected-warning {{'swiftcall' only applies to function types; type here is 'int'}} +void variadic(int x, ...) SWIFTCALL; // expected-error {{variadic function cannot use swiftcall calling convention}} +void multiple_ccs(int x) SWIFTCALL __attribute__((vectorcall)); // expected-error {{vectorcall and swiftcall attributes are not compatible}} +void (*functionPointer)(void) SWIFTCALL; + +void indirect_result_nonswift(INDIRECT_RESULT void *out); // expected-error {{'swift_indirect_result' parameter can only be used with swiftcall calling convention}} +void indirect_result_bad_position(int first, INDIRECT_RESULT void *out) SWIFTCALL; // expected-error {{'swift_indirect_result' parameters must be first parameters of function}} +void indirect_result_bad_type(INDIRECT_RESULT int out) SWIFTCALL; // expected-error {{'swift_indirect_result' parameter must have pointer type; type here is 'int'}} +void indirect_result_single(INDIRECT_RESULT void *out) SWIFTCALL; +void indirect_result_multiple(INDIRECT_RESULT void *out1, INDIRECT_RESULT void *out2) SWIFTCALL; + +void error_result_nonswift(ERROR_RESULT void **error); // expected-error {{'swift_error_result' parameter can only be used with swiftcall calling convention}} expected-error{{'swift_error_result' parameter must follow 'swift_context' parameter}} +void error_result_bad_position(ERROR_RESULT void **error, int last) SWIFTCALL; // expected-error {{'swift_error_result' parameter must be last parameter of function}} +void error_result_bad_position2(int first, ERROR_RESULT void **error) SWIFTCALL; // expected-error {{'swift_error_result' parameter must follow 'swift_context' parameter}} +void error_result_bad_type(CONTEXT void *context, ERROR_RESULT int error) SWIFTCALL; // expected-error {{'swift_error_result' parameter must have pointer to unqualified pointer type; type here is 'int'}} +void error_result_bad_type2(CONTEXT void *context, ERROR_RESULT int *error) SWIFTCALL; // expected-error {{'swift_error_result' parameter must have pointer to unqualified pointer type; type here is 'int *'}} +void error_result_okay(int a, int b, CONTEXT void *context, ERROR_RESULT void **error) SWIFTCALL; + +void context_nonswift(CONTEXT void *context); // expected-error {{'swift_context' parameter can only be used with swiftcall calling convention}} +void context_bad_position(CONTEXT void *context, int x) SWIFTCALL; // expected-error {{'swift_context' parameter can only be followed by 'swift_error_result' parameter}} +void context_bad_type(CONTEXT int context) SWIFTCALL; // expected-error {{'swift_context' parameter must have pointer type; type here is 'int'}} +void context_okay(CONTEXT void *context) SWIFTCALL; + +template void indirect_result_temp_okay1(INDIRECT_RESULT T *out) SWIFTCALL; +template void indirect_result_temp_okay2(INDIRECT_RESULT T out) SWIFTCALL; // expected-note {{candidate template ignored: substitution failure [with T = int]: 'swift_indirect_result' parameter must have pointer type; type here is 'int'}} +void test_indirect_result_temp(void *out) { + indirect_result_temp_okay1(out); + indirect_result_temp_okay2(out); + indirect_result_temp_okay2(1); // expected-error {{no matching function for call to 'indirect_result_temp_okay2'}} +} diff --git a/tools/libclang/CXType.cpp b/tools/libclang/CXType.cpp index d6d5daa0f9d..5cde2360552 100644 --- a/tools/libclang/CXType.cpp +++ b/tools/libclang/CXType.cpp @@ -533,6 +533,7 @@ CXCallingConv clang_getFunctionTypeCallingConv(CXType X) { TCALLINGCONV(AAPCS); TCALLINGCONV(AAPCS_VFP); TCALLINGCONV(IntelOclBicc); + TCALLINGCONV(Swift); TCALLINGCONV(PreserveMost); TCALLINGCONV(PreserveAll); case CC_SpirFunction: return CXCallingConv_Unexposed; diff --git a/utils/TableGen/ClangAttrEmitter.cpp b/utils/TableGen/ClangAttrEmitter.cpp index 165f9e6440b..16cffbde858 100644 --- a/utils/TableGen/ClangAttrEmitter.cpp +++ b/utils/TableGen/ClangAttrEmitter.cpp @@ -1749,7 +1749,8 @@ namespace { static const AttrClassDescriptor AttrClassDescriptors[] = { { "ATTR", "Attr" }, { "INHERITABLE_ATTR", "InheritableAttr" }, - { "INHERITABLE_PARAM_ATTR", "InheritableParamAttr" } + { "INHERITABLE_PARAM_ATTR", "InheritableParamAttr" }, + { "PARAMETER_ABI_ATTR", "ParameterABIAttr" } }; static void emitDefaultDefine(raw_ostream &OS, StringRef name, From c806483c18fee9e4703540993ad9a65f54e69f82 Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 11 Mar 2016 04:30:31 +0000 Subject: [PATCH 424/742] Preserve ExtParameterInfos into CGFunctionInfo. As part of this, make the function-arrangement interfaces a little simpler and more semantic. NFC. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263191 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/CanonicalType.h | 3 + include/clang/CodeGen/CGFunctionInfo.h | 34 +++- lib/CodeGen/CGAtomic.cpp | 3 +- lib/CodeGen/CGBlocks.cpp | 21 ++- lib/CodeGen/CGBuiltin.cpp | 4 +- lib/CodeGen/CGCall.cpp | 227 ++++++++++++++++++++----- lib/CodeGen/CGDeclCXX.cpp | 4 +- lib/CodeGen/CGException.cpp | 10 +- lib/CodeGen/CGObjC.cpp | 46 ++--- lib/CodeGen/CGObjCMac.cpp | 31 ++-- lib/CodeGen/CGObjCRuntime.cpp | 20 +-- lib/CodeGen/CGOpenMPRuntime.cpp | 34 ++-- lib/CodeGen/CGStmt.cpp | 3 +- lib/CodeGen/CGStmtOpenMP.cpp | 3 +- lib/CodeGen/CodeGenABITypes.cpp | 2 +- lib/CodeGen/CodeGenTypes.h | 62 +++++-- lib/CodeGen/ItaniumCXXABI.cpp | 9 +- 17 files changed, 331 insertions(+), 185 deletions(-) diff --git a/include/clang/AST/CanonicalType.h b/include/clang/AST/CanonicalType.h index b25800bfedb..77510afeec1 100644 --- a/include/clang/AST/CanonicalType.h +++ b/include/clang/AST/CanonicalType.h @@ -484,6 +484,9 @@ struct CanProxyAdaptor LLVM_CLANG_CANPROXY_TYPE_ACCESSOR(getReturnType) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(FunctionType::ExtInfo, getExtInfo) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(unsigned, getNumParams) + LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, hasExtParameterInfos) + LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR( + ArrayRef, getExtParameterInfos) CanQualType getParamType(unsigned i) const { return CanQualType::CreateUnsafe(this->getTypePtr()->getParamType(i)); } diff --git a/include/clang/CodeGen/CGFunctionInfo.h b/include/clang/CodeGen/CGFunctionInfo.h index bb6ceb43514..9af28975bdd 100644 --- a/include/clang/CodeGen/CGFunctionInfo.h +++ b/include/clang/CodeGen/CGFunctionInfo.h @@ -338,6 +338,7 @@ class CGFunctionInfo : public llvm::FoldingSetNode { CanQualType type; ABIArgInfo info; }; + typedef FunctionProtoType::ExtParameterInfo ExtParameterInfo; /// The LLVM::CallingConv to use for this function (as specified by the /// user). @@ -371,7 +372,8 @@ class CGFunctionInfo : public llvm::FoldingSetNode { /// The struct representing all arguments passed in memory. Only used when /// passing non-trivial types with inalloca. Not part of the profile. llvm::StructType *ArgStruct; - unsigned ArgStructAlign; + unsigned ArgStructAlign : 31; + unsigned HasExtParameterInfos : 1; unsigned NumArgs; ArgInfo *getArgsBuffer() { @@ -381,6 +383,14 @@ class CGFunctionInfo : public llvm::FoldingSetNode { return reinterpret_cast(this + 1); } + ExtParameterInfo *getExtParameterInfosBuffer() { + return reinterpret_cast(getArgsBuffer() + NumArgs + 1); + } + const ExtParameterInfo *getExtParameterInfosBuffer() const{ + return reinterpret_cast( + getArgsBuffer() + NumArgs + 1); + } + CGFunctionInfo() : Required(RequiredArgs::All) {} public: @@ -388,6 +398,7 @@ class CGFunctionInfo : public llvm::FoldingSetNode { bool instanceMethod, bool chainCall, const FunctionType::ExtInfo &extInfo, + ArrayRef paramInfos, CanQualType resultType, ArrayRef argTypes, RequiredArgs required); @@ -460,6 +471,16 @@ class CGFunctionInfo : public llvm::FoldingSetNode { ABIArgInfo &getReturnInfo() { return getArgsBuffer()[0].info; } const ABIArgInfo &getReturnInfo() const { return getArgsBuffer()[0].info; } + ArrayRef getExtParameterInfos() const { + if (!HasExtParameterInfos) return {}; + return llvm::makeArrayRef(getExtParameterInfosBuffer(), NumArgs); + } + ExtParameterInfo getExtParameterInfo(unsigned argIndex) const { + assert(argIndex <= NumArgs); + if (!HasExtParameterInfos) return ExtParameterInfo(); + return getExtParameterInfos()[argIndex]; + } + /// \brief Return true if this function uses inalloca arguments. bool usesInAlloca() const { return ArgStruct; } @@ -482,6 +503,11 @@ class CGFunctionInfo : public llvm::FoldingSetNode { ID.AddBoolean(HasRegParm); ID.AddInteger(RegParm); ID.AddInteger(Required.getOpaqueData()); + ID.AddBoolean(HasExtParameterInfos); + if (HasExtParameterInfos) { + for (auto paramInfo : getExtParameterInfos()) + ID.AddInteger(paramInfo.getOpaqueValue()); + } getReturnType().Profile(ID); for (const auto &I : arguments()) I.type.Profile(ID); @@ -490,6 +516,7 @@ class CGFunctionInfo : public llvm::FoldingSetNode { bool InstanceMethod, bool ChainCall, const FunctionType::ExtInfo &info, + ArrayRef paramInfos, RequiredArgs required, CanQualType resultType, ArrayRef argTypes) { @@ -501,6 +528,11 @@ class CGFunctionInfo : public llvm::FoldingSetNode { ID.AddBoolean(info.getHasRegParm()); ID.AddInteger(info.getRegParm()); ID.AddInteger(required.getOpaqueData()); + ID.AddBoolean(!paramInfos.empty()); + if (!paramInfos.empty()) { + for (auto paramInfo : paramInfos) + ID.AddInteger(paramInfo.getOpaqueValue()); + } resultType.Profile(ID); for (ArrayRef::iterator i = argTypes.begin(), e = argTypes.end(); i != e; ++i) { diff --git a/lib/CodeGen/CGAtomic.cpp b/lib/CodeGen/CGAtomic.cpp index 1ef5d1035a0..f6552957e70 100644 --- a/lib/CodeGen/CGAtomic.cpp +++ b/lib/CodeGen/CGAtomic.cpp @@ -323,8 +323,7 @@ static RValue emitAtomicLibcall(CodeGenFunction &CGF, QualType resultType, CallArgList &args) { const CGFunctionInfo &fnInfo = - CGF.CGM.getTypes().arrangeFreeFunctionCall(resultType, args, - FunctionType::ExtInfo(), RequiredArgs::All); + CGF.CGM.getTypes().arrangeBuiltinFunctionCall(resultType, args); llvm::FunctionType *fnTy = CGF.CGM.getTypes().GetFunctionType(fnInfo); llvm::Constant *fn = CGF.CGM.CreateRuntimeFunction(fnTy, fnName); return CGF.EmitCall(fnInfo, fn, ReturnValueSlot(), args); diff --git a/lib/CodeGen/CGBlocks.cpp b/lib/CodeGen/CGBlocks.cpp index 742a5236e9f..e4f12b1d001 100644 --- a/lib/CodeGen/CGBlocks.cpp +++ b/lib/CodeGen/CGBlocks.cpp @@ -1174,9 +1174,8 @@ CodeGenFunction::GenerateBlockFunction(GlobalDecl GD, // Create the function declaration. const FunctionProtoType *fnType = blockInfo.getBlockExpr()->getFunctionType(); - const CGFunctionInfo &fnInfo = CGM.getTypes().arrangeFreeFunctionDeclaration( - fnType->getReturnType(), args, fnType->getExtInfo(), - fnType->isVariadic()); + const CGFunctionInfo &fnInfo = + CGM.getTypes().arrangeBlockFunctionDeclaration(fnType, args); if (CGM.ReturnSlotInterferesWithArgs(fnInfo)) blockInfo.UsesStret = true; @@ -1329,8 +1328,8 @@ CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) { C.VoidPtrTy); args.push_back(&srcDecl); - const CGFunctionInfo &FI = CGM.getTypes().arrangeFreeFunctionDeclaration( - C.VoidTy, args, FunctionType::ExtInfo(), /*variadic=*/false); + const CGFunctionInfo &FI = + CGM.getTypes().arrangeBuiltinFunctionDeclaration(C.VoidTy, args); // FIXME: it would be nice if these were mergeable with things with // identical semantics. @@ -1505,8 +1504,8 @@ CodeGenFunction::GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo) { C.VoidPtrTy); args.push_back(&srcDecl); - const CGFunctionInfo &FI = CGM.getTypes().arrangeFreeFunctionDeclaration( - C.VoidTy, args, FunctionType::ExtInfo(), /*variadic=*/false); + const CGFunctionInfo &FI = + CGM.getTypes().arrangeBuiltinFunctionDeclaration(C.VoidTy, args); // FIXME: We'd like to put these into a mergable by content, with // internal linkage. @@ -1791,8 +1790,8 @@ generateByrefCopyHelper(CodeGenFunction &CGF, const BlockByrefInfo &byrefInfo, Context.VoidPtrTy); args.push_back(&src); - const CGFunctionInfo &FI = CGF.CGM.getTypes().arrangeFreeFunctionDeclaration( - R, args, FunctionType::ExtInfo(), /*variadic=*/false); + const CGFunctionInfo &FI = + CGF.CGM.getTypes().arrangeBuiltinFunctionDeclaration(R, args); llvm::FunctionType *LTy = CGF.CGM.getTypes().GetFunctionType(FI); @@ -1864,8 +1863,8 @@ generateByrefDisposeHelper(CodeGenFunction &CGF, Context.VoidPtrTy); args.push_back(&src); - const CGFunctionInfo &FI = CGF.CGM.getTypes().arrangeFreeFunctionDeclaration( - R, args, FunctionType::ExtInfo(), /*variadic=*/false); + const CGFunctionInfo &FI = + CGF.CGM.getTypes().arrangeBuiltinFunctionDeclaration(R, args); llvm::FunctionType *LTy = CGF.CGM.getTypes().GetFunctionType(FI); diff --git a/lib/CodeGen/CGBuiltin.cpp b/lib/CodeGen/CGBuiltin.cpp index 787ac5361bb..c73aa222b78 100644 --- a/lib/CodeGen/CGBuiltin.cpp +++ b/lib/CodeGen/CGBuiltin.cpp @@ -1294,9 +1294,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, Args.add(RValue::get(llvm::Constant::getNullValue(VoidPtrTy)), getContext().VoidPtrTy); const CGFunctionInfo &FuncInfo = - CGM.getTypes().arrangeFreeFunctionCall(E->getType(), Args, - FunctionType::ExtInfo(), - RequiredArgs::All); + CGM.getTypes().arrangeBuiltinFunctionCall(E->getType(), Args); llvm::FunctionType *FTy = CGM.getTypes().GetFunctionType(FuncInfo); llvm::Constant *Func = CGM.CreateRuntimeFunction(FTy, LibCallName); return EmitCall(FuncInfo, Func, ReturnValueSlot(), Args); diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index 8a9c8cd3fe4..8a4838945b1 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -92,15 +92,25 @@ CodeGenTypes::arrangeFreeFunctionType(CanQual FTNP) { return arrangeLLVMFunctionInfo(FTNP->getReturnType().getUnqualifiedType(), /*instanceMethod=*/false, /*chainCall=*/false, None, - FTNP->getExtInfo(), RequiredArgs(0)); + FTNP->getExtInfo(), {}, RequiredArgs(0)); } /// Adds the formal paramaters in FPT to the given prefix. If any parameter in /// FPT has pass_object_size attrs, then we'll add parameters for those, too. static void appendParameterTypes(const CodeGenTypes &CGT, SmallVectorImpl &prefix, - const CanQual &FPT, + SmallVectorImpl ¶mInfos, + CanQual FPT, const FunctionDecl *FD) { + // Fill out paramInfos. + if (FPT->hasExtParameterInfos() || !paramInfos.empty()) { + assert(paramInfos.size() <= prefix.size()); + auto protoParamInfos = FPT->getExtParameterInfos(); + paramInfos.reserve(prefix.size() + protoParamInfos.size()); + paramInfos.resize(prefix.size()); + paramInfos.append(paramInfos.begin(), paramInfos.end()); + } + // Fast path: unknown target. if (FD == nullptr) { prefix.append(FPT->param_type_begin(), FPT->param_type_end()); @@ -127,13 +137,16 @@ arrangeLLVMFunctionInfo(CodeGenTypes &CGT, bool instanceMethod, SmallVectorImpl &prefix, CanQual FTP, const FunctionDecl *FD) { + SmallVector paramInfos; RequiredArgs required = RequiredArgs::forPrototypePlus(FTP, prefix.size()); // FIXME: Kill copy. - appendParameterTypes(CGT, prefix, FTP, FD); + appendParameterTypes(CGT, prefix, paramInfos, FTP, FD); CanQualType resultType = FTP->getReturnType().getUnqualifiedType(); + return CGT.arrangeLLVMFunctionInfo(resultType, instanceMethod, /*chainCall=*/false, prefix, - FTP->getExtInfo(), required); + FTP->getExtInfo(), paramInfos, + required); } /// Arrange the argument and result information for a value of the @@ -232,6 +245,7 @@ CodeGenTypes::arrangeCXXStructorDeclaration(const CXXMethodDecl *MD, StructorType Type) { SmallVector argTypes; + SmallVector paramInfos; argTypes.push_back(GetThisType(Context, MD->getParent())); GlobalDecl GD; @@ -245,7 +259,7 @@ CodeGenTypes::arrangeCXXStructorDeclaration(const CXXMethodDecl *MD, CanQual FTP = GetFormalType(MD); // Add the formal parameters. - appendParameterTypes(*this, argTypes, FTP, MD); + appendParameterTypes(*this, argTypes, paramInfos, FTP, MD); TheCXXABI.buildStructorSignature(MD, Type, argTypes); @@ -260,7 +274,53 @@ CodeGenTypes::arrangeCXXStructorDeclaration(const CXXMethodDecl *MD, : Context.VoidTy; return arrangeLLVMFunctionInfo(resultType, /*instanceMethod=*/true, /*chainCall=*/false, argTypes, extInfo, - required); + paramInfos, required); +} + +static SmallVector +getArgTypesForCall(ASTContext &ctx, const CallArgList &args) { + SmallVector argTypes; + for (auto &arg : args) + argTypes.push_back(ctx.getCanonicalParamType(arg.Ty)); + return argTypes; +} + +static SmallVector +getArgTypesForDeclaration(ASTContext &ctx, const FunctionArgList &args) { + SmallVector argTypes; + for (auto &arg : args) + argTypes.push_back(ctx.getCanonicalParamType(arg->getType())); + return argTypes; +} + +static void addExtParameterInfosForCall( + llvm::SmallVectorImpl ¶mInfos, + const FunctionProtoType *proto, + unsigned prefixArgs, + unsigned totalArgs) { + assert(proto->hasExtParameterInfos()); + assert(paramInfos.size() <= prefixArgs); + assert(proto->getNumParams() + prefixArgs <= totalArgs); + + // Add default infos for any prefix args that don't already have infos. + paramInfos.resize(prefixArgs); + + // Add infos for the prototype. + auto protoInfos = proto->getExtParameterInfos(); + paramInfos.append(protoInfos.begin(), protoInfos.end()); + + // Add default infos for the variadic arguments. + paramInfos.resize(totalArgs); +} + +static llvm::SmallVector +getExtParameterInfosForCall(const FunctionProtoType *proto, + unsigned prefixArgs, unsigned totalArgs) { + llvm::SmallVector result; + if (proto->hasExtParameterInfos()) { + addExtParameterInfosForCall(result, proto, prefixArgs, totalArgs); + } + return result; } /// Arrange a call to a C++ method, passing the given arguments. @@ -284,9 +344,11 @@ CodeGenTypes::arrangeCXXConstructorCall(const CallArgList &args, : Context.VoidTy; FunctionType::ExtInfo Info = FPT->getExtInfo(); + auto ParamInfos = getExtParameterInfosForCall(FPT.getTypePtr(), 1 + ExtraArgs, + ArgTypes.size()); return arrangeLLVMFunctionInfo(ResultType, /*instanceMethod=*/true, /*chainCall=*/false, ArgTypes, Info, - Required); + ParamInfos, Required); } /// Arrange the argument and result information for the declaration or @@ -307,7 +369,7 @@ CodeGenTypes::arrangeFunctionDeclaration(const FunctionDecl *FD) { CanQual noProto = FTy.getAs(); return arrangeLLVMFunctionInfo( noProto->getReturnType(), /*instanceMethod=*/false, - /*chainCall=*/false, None, noProto->getExtInfo(), RequiredArgs::All); + /*chainCall=*/false, None, noProto->getExtInfo(), {},RequiredArgs::All); } assert(isa(FTy)); @@ -353,7 +415,18 @@ CodeGenTypes::arrangeObjCMessageSendSignature(const ObjCMethodDecl *MD, return arrangeLLVMFunctionInfo( GetReturnType(MD->getReturnType()), /*instanceMethod=*/false, - /*chainCall=*/false, argTys, einfo, required); + /*chainCall=*/false, argTys, einfo, {}, required); +} + +const CGFunctionInfo & +CodeGenTypes::arrangeUnprototypedObjCMessageSend(QualType returnType, + const CallArgList &args) { + auto argTypes = getArgTypesForCall(Context, args); + FunctionType::ExtInfo einfo; + + return arrangeLLVMFunctionInfo( + GetReturnType(returnType), /*instanceMethod=*/false, + /*chainCall=*/false, argTypes, einfo, {}, RequiredArgs::All); } const CGFunctionInfo & @@ -382,7 +455,7 @@ CodeGenTypes::arrangeMSMemberPointerThunk(const CXXMethodDecl *MD) { CanQualType ArgTys[] = { GetThisType(Context, MD->getParent()) }; return arrangeLLVMFunctionInfo(Context.VoidTy, /*instanceMethod=*/false, /*chainCall=*/false, ArgTys, - FTP->getExtInfo(), RequiredArgs(1)); + FTP->getExtInfo(), {}, RequiredArgs(1)); } const CGFunctionInfo & @@ -402,7 +475,8 @@ CodeGenTypes::arrangeMSCtorClosure(const CXXConstructorDecl *CD, /*IsVariadic=*/false, /*IsCXXMethod=*/true); return arrangeLLVMFunctionInfo(Context.VoidTy, /*instanceMethod=*/true, /*chainCall=*/false, ArgTys, - FunctionType::ExtInfo(CC), RequiredArgs::All); + FunctionType::ExtInfo(CC), {}, + RequiredArgs::All); } /// Arrange a call as unto a free function, except possibly with an @@ -416,6 +490,8 @@ arrangeFreeFunctionLikeCall(CodeGenTypes &CGT, bool chainCall) { assert(args.size() >= numExtraRequiredArgs); + llvm::SmallVector paramInfos; + // In most cases, there are no optional arguments. RequiredArgs required = RequiredArgs::All; @@ -425,6 +501,10 @@ arrangeFreeFunctionLikeCall(CodeGenTypes &CGT, if (proto->isVariadic()) required = RequiredArgs(proto->getNumParams() + numExtraRequiredArgs); + if (proto->hasExtParameterInfos()) + addExtParameterInfosForCall(paramInfos, proto, numExtraRequiredArgs, + args.size()); + // If we don't have a prototype at all, but we're supposed to // explicitly use the variadic convention for unprototyped calls, // treat all of the arguments as required but preserve the nominal @@ -441,7 +521,8 @@ arrangeFreeFunctionLikeCall(CodeGenTypes &CGT, argTypes.push_back(CGT.getContext().getCanonicalParamType(arg.Ty)); return CGT.arrangeLLVMFunctionInfo(GetReturnType(fnType->getReturnType()), /*instanceMethod=*/false, chainCall, - argTypes, fnType->getExtInfo(), required); + argTypes, fnType->getExtInfo(), paramInfos, + required); } /// Figure out the rules for calling a function with the given formal @@ -456,7 +537,7 @@ CodeGenTypes::arrangeFreeFunctionCall(const CallArgList &args, chainCall ? 1 : 0, chainCall); } -/// A block function call is essentially a free-function call with an +/// A block function is essentially a free function with an /// extra implicit argument. const CGFunctionInfo & CodeGenTypes::arrangeBlockFunctionCall(const CallArgList &args, @@ -466,54 +547,99 @@ CodeGenTypes::arrangeBlockFunctionCall(const CallArgList &args, } const CGFunctionInfo & -CodeGenTypes::arrangeFreeFunctionCall(QualType resultType, - const CallArgList &args, - FunctionType::ExtInfo info, - RequiredArgs required) { +CodeGenTypes::arrangeBlockFunctionDeclaration(const FunctionProtoType *proto, + const FunctionArgList ¶ms) { + auto paramInfos = getExtParameterInfosForCall(proto, 1, params.size()); + auto argTypes = getArgTypesForDeclaration(Context, params); + + return arrangeLLVMFunctionInfo(GetReturnType(proto->getReturnType()), + /*instanceMethod*/ false, /*chainCall*/ false, + argTypes, proto->getExtInfo(), paramInfos, + RequiredArgs::forPrototypePlus(proto, 1)); +} + +const CGFunctionInfo & +CodeGenTypes::arrangeBuiltinFunctionCall(QualType resultType, + const CallArgList &args) { // FIXME: Kill copy. SmallVector argTypes; for (const auto &Arg : args) argTypes.push_back(Context.getCanonicalParamType(Arg.Ty)); return arrangeLLVMFunctionInfo( GetReturnType(resultType), /*instanceMethod=*/false, - /*chainCall=*/false, argTypes, info, required); + /*chainCall=*/false, argTypes, FunctionType::ExtInfo(), + /*paramInfos=*/ {}, RequiredArgs::All); } -/// Arrange a call to a C++ method, passing the given arguments. const CGFunctionInfo & -CodeGenTypes::arrangeCXXMethodCall(const CallArgList &args, - const FunctionProtoType *FPT, - RequiredArgs required) { - // FIXME: Kill copy. - SmallVector argTypes; - for (const auto &Arg : args) - argTypes.push_back(Context.getCanonicalParamType(Arg.Ty)); +CodeGenTypes::arrangeBuiltinFunctionDeclaration(QualType resultType, + const FunctionArgList &args) { + auto argTypes = getArgTypesForDeclaration(Context, args); + + return arrangeLLVMFunctionInfo( + GetReturnType(resultType), /*instanceMethod=*/false, /*chainCall=*/false, + argTypes, FunctionType::ExtInfo(), {}, RequiredArgs::All); +} - FunctionType::ExtInfo info = FPT->getExtInfo(); +const CGFunctionInfo & +CodeGenTypes::arrangeBuiltinFunctionDeclaration(CanQualType resultType, + ArrayRef argTypes) { return arrangeLLVMFunctionInfo( - GetReturnType(FPT->getReturnType()), /*instanceMethod=*/true, - /*chainCall=*/false, argTypes, info, required); + resultType, /*instanceMethod=*/false, /*chainCall=*/false, + argTypes, FunctionType::ExtInfo(), {}, RequiredArgs::All); } -const CGFunctionInfo &CodeGenTypes::arrangeFreeFunctionDeclaration( - QualType resultType, const FunctionArgList &args, - const FunctionType::ExtInfo &info, bool isVariadic) { + +/// Arrange a call to a C++ method, passing the given arguments. +const CGFunctionInfo & +CodeGenTypes::arrangeCXXMethodCall(const CallArgList &args, + const FunctionProtoType *proto, + RequiredArgs required) { + unsigned numRequiredArgs = + (proto->isVariadic() ? required.getNumRequiredArgs() : args.size()); + unsigned numPrefixArgs = numRequiredArgs - proto->getNumParams(); + auto paramInfos = + getExtParameterInfosForCall(proto, numPrefixArgs, args.size()); + // FIXME: Kill copy. - SmallVector argTypes; - for (auto Arg : args) - argTypes.push_back(Context.getCanonicalParamType(Arg->getType())); + auto argTypes = getArgTypesForCall(Context, args); - RequiredArgs required = - (isVariadic ? RequiredArgs(args.size()) : RequiredArgs::All); + FunctionType::ExtInfo info = proto->getExtInfo(); return arrangeLLVMFunctionInfo( - GetReturnType(resultType), /*instanceMethod=*/false, - /*chainCall=*/false, argTypes, info, required); + GetReturnType(proto->getReturnType()), /*instanceMethod=*/true, + /*chainCall=*/false, argTypes, info, paramInfos, required); } const CGFunctionInfo &CodeGenTypes::arrangeNullaryFunction() { return arrangeLLVMFunctionInfo( getContext().VoidTy, /*instanceMethod=*/false, /*chainCall=*/false, - None, FunctionType::ExtInfo(), RequiredArgs::All); + None, FunctionType::ExtInfo(), {}, RequiredArgs::All); +} + +const CGFunctionInfo & +CodeGenTypes::arrangeCall(const CGFunctionInfo &signature, + const CallArgList &args) { + assert(signature.arg_size() <= args.size()); + if (signature.arg_size() == args.size()) + return signature; + + SmallVector paramInfos; + auto sigParamInfos = signature.getExtParameterInfos(); + if (!sigParamInfos.empty()) { + paramInfos.append(sigParamInfos.begin(), sigParamInfos.end()); + paramInfos.resize(args.size()); + } + + auto argTypes = getArgTypesForCall(Context, args); + + assert(signature.getRequiredArgs().allowsOptionalArgs()); + return arrangeLLVMFunctionInfo(signature.getReturnType(), + signature.isInstanceMethod(), + signature.isChainCall(), + argTypes, + signature.getExtInfo(), + paramInfos, + signature.getRequiredArgs()); } /// Arrange the argument and result information for an abstract value @@ -525,25 +651,26 @@ CodeGenTypes::arrangeLLVMFunctionInfo(CanQualType resultType, bool chainCall, ArrayRef argTypes, FunctionType::ExtInfo info, + ArrayRef paramInfos, RequiredArgs required) { assert(std::all_of(argTypes.begin(), argTypes.end(), std::mem_fun_ref(&CanQualType::isCanonicalAsParam))); - unsigned CC = ClangCallConvToLLVMCallConv(info.getCC()); - // Lookup or create unique function info. llvm::FoldingSetNodeID ID; - CGFunctionInfo::Profile(ID, instanceMethod, chainCall, info, required, - resultType, argTypes); + CGFunctionInfo::Profile(ID, instanceMethod, chainCall, info, paramInfos, + required, resultType, argTypes); void *insertPos = nullptr; CGFunctionInfo *FI = FunctionInfos.FindNodeOrInsertPos(ID, insertPos); if (FI) return *FI; + unsigned CC = ClangCallConvToLLVMCallConv(info.getCC()); + // Construct the function info. We co-allocate the ArgInfos. FI = CGFunctionInfo::create(CC, instanceMethod, chainCall, info, - resultType, argTypes, required); + paramInfos, resultType, argTypes, required); FunctionInfos.InsertNode(FI, insertPos); bool inserted = FunctionsBeingProcessed.insert(FI).second; @@ -574,11 +701,16 @@ CGFunctionInfo *CGFunctionInfo::create(unsigned llvmCC, bool instanceMethod, bool chainCall, const FunctionType::ExtInfo &info, + ArrayRef paramInfos, CanQualType resultType, ArrayRef argTypes, RequiredArgs required) { + assert(paramInfos.empty() || paramInfos.size() == argTypes.size()); + void *buffer = operator new(sizeof(CGFunctionInfo) + - sizeof(ArgInfo) * (argTypes.size() + 1)); + sizeof(ArgInfo) * (argTypes.size() + 1) + + sizeof(ExtParameterInfo) * paramInfos.size()); + CGFunctionInfo *FI = new(buffer) CGFunctionInfo(); FI->CallingConvention = llvmCC; FI->EffectiveCallingConvention = llvmCC; @@ -593,9 +725,12 @@ CGFunctionInfo *CGFunctionInfo::create(unsigned llvmCC, FI->ArgStruct = nullptr; FI->ArgStructAlign = 0; FI->NumArgs = argTypes.size(); + FI->HasExtParameterInfos = !paramInfos.empty(); FI->getArgsBuffer()[0].type = resultType; for (unsigned i = 0, e = argTypes.size(); i != e; ++i) FI->getArgsBuffer()[i + 1].type = argTypes[i]; + for (unsigned i = 0, e = paramInfos.size(); i != e; ++i) + FI->getExtParameterInfosBuffer()[i] = paramInfos[i]; return FI; } diff --git a/lib/CodeGen/CGDeclCXX.cpp b/lib/CodeGen/CGDeclCXX.cpp index adba7316879..50454cabd9c 100644 --- a/lib/CodeGen/CGDeclCXX.cpp +++ b/lib/CodeGen/CGDeclCXX.cpp @@ -587,8 +587,8 @@ llvm::Function *CodeGenFunction::generateDestroyHelper( getContext().VoidPtrTy); args.push_back(&dst); - const CGFunctionInfo &FI = CGM.getTypes().arrangeFreeFunctionDeclaration( - getContext().VoidTy, args, FunctionType::ExtInfo(), /*variadic=*/false); + const CGFunctionInfo &FI = + CGM.getTypes().arrangeBuiltinFunctionDeclaration(getContext().VoidTy, args); llvm::FunctionType *FTy = CGM.getTypes().GetFunctionType(FI); llvm::Function *fn = CGM.CreateGlobalInitOrDestructFunction( FTy, "__cxx_global_array_dtor", FI, VD->getLocation()); diff --git a/lib/CodeGen/CGException.cpp b/lib/CodeGen/CGException.cpp index fce2e758196..fb51262300e 100644 --- a/lib/CodeGen/CGException.cpp +++ b/lib/CodeGen/CGException.cpp @@ -1422,12 +1422,8 @@ struct PerformSEHFinally final : EHScopeStack::Cleanup { Args.add(RValue::get(FP), ArgTys[1]); // Arrange a two-arg function info and type. - FunctionProtoType::ExtProtoInfo EPI; - const auto *FPT = cast( - Context.getFunctionType(Context.VoidTy, ArgTys, EPI)); const CGFunctionInfo &FnInfo = - CGM.getTypes().arrangeFreeFunctionCall(Args, FPT, - /*chainCall=*/false); + CGM.getTypes().arrangeBuiltinFunctionCall(Context.VoidTy, Args); CGF.EmitCall(FnInfo, OutlinedFinally, ReturnValueSlot(), Args); } @@ -1656,8 +1652,8 @@ void CodeGenFunction::startOutlinedSEHHelper(CodeGenFunction &ParentCGF, QualType RetTy = IsFilter ? getContext().LongTy : getContext().VoidTy; llvm::Function *ParentFn = ParentCGF.CurFn; - const CGFunctionInfo &FnInfo = CGM.getTypes().arrangeFreeFunctionDeclaration( - RetTy, Args, FunctionType::ExtInfo(), /*isVariadic=*/false); + const CGFunctionInfo &FnInfo = + CGM.getTypes().arrangeBuiltinFunctionDeclaration(RetTy, Args); llvm::FunctionType *FnTy = CGM.getTypes().GetFunctionType(FnInfo); llvm::Function *Fn = llvm::Function::Create( diff --git a/lib/CodeGen/CGObjC.cpp b/lib/CodeGen/CGObjC.cpp index 989d911a0cb..1c165594612 100644 --- a/lib/CodeGen/CGObjC.cpp +++ b/lib/CodeGen/CGObjC.cpp @@ -590,9 +590,7 @@ static void emitStructGetterCall(CodeGenFunction &CGF, ObjCIvarDecl *ivar, args.add(RValue::get(CGF.Builder.getInt1(hasStrong)), Context.BoolTy); llvm::Value *fn = CGF.CGM.getObjCRuntime().GetGetStructFunction(); - CGF.EmitCall(CGF.getTypes().arrangeFreeFunctionCall(Context.VoidTy, args, - FunctionType::ExtInfo(), - RequiredArgs::All), + CGF.EmitCall(CGF.getTypes().arrangeBuiltinFunctionCall(Context.VoidTy, args), fn, ReturnValueSlot(), args); } @@ -856,10 +854,8 @@ static void emitCPPObjectAtomicGetterCall(CodeGenFunction &CGF, llvm::Value *copyCppAtomicObjectFn = CGF.CGM.getObjCRuntime().GetCppAtomicObjectGetFunction(); - CGF.EmitCall(CGF.getTypes().arrangeFreeFunctionCall(CGF.getContext().VoidTy, - args, - FunctionType::ExtInfo(), - RequiredArgs::All), + CGF.EmitCall( + CGF.getTypes().arrangeBuiltinFunctionCall(CGF.getContext().VoidTy, args), copyCppAtomicObjectFn, ReturnValueSlot(), args); } @@ -950,8 +946,7 @@ CodeGenFunction::generateObjCGetterBody(const ObjCImplementationDecl *classImpl, // runtime already should have computed it to build the function. llvm::Instruction *CallInstruction; RValue RV = EmitCall( - getTypes().arrangeFreeFunctionCall( - propType, args, FunctionType::ExtInfo(), RequiredArgs::All), + getTypes().arrangeBuiltinFunctionCall(propType, args), getPropertyFn, ReturnValueSlot(), args, CGCalleeInfo(), &CallInstruction); if (llvm::CallInst *call = dyn_cast(CallInstruction)) @@ -1067,10 +1062,8 @@ static void emitStructSetterCall(CodeGenFunction &CGF, ObjCMethodDecl *OMD, args.add(RValue::get(CGF.Builder.getFalse()), CGF.getContext().BoolTy); llvm::Value *copyStructFn = CGF.CGM.getObjCRuntime().GetSetStructFunction(); - CGF.EmitCall(CGF.getTypes().arrangeFreeFunctionCall(CGF.getContext().VoidTy, - args, - FunctionType::ExtInfo(), - RequiredArgs::All), + CGF.EmitCall( + CGF.getTypes().arrangeBuiltinFunctionCall(CGF.getContext().VoidTy, args), copyStructFn, ReturnValueSlot(), args); } @@ -1105,10 +1098,8 @@ static void emitCPPObjectAtomicSetterCall(CodeGenFunction &CGF, llvm::Value *copyCppAtomicObjectFn = CGF.CGM.getObjCRuntime().GetCppAtomicObjectSetFunction(); - CGF.EmitCall(CGF.getTypes().arrangeFreeFunctionCall(CGF.getContext().VoidTy, - args, - FunctionType::ExtInfo(), - RequiredArgs::All), + CGF.EmitCall( + CGF.getTypes().arrangeBuiltinFunctionCall(CGF.getContext().VoidTy, args), copyCppAtomicObjectFn, ReturnValueSlot(), args); } @@ -1238,9 +1229,7 @@ CodeGenFunction::generateObjCSetterBody(const ObjCImplementationDecl *classImpl, if (setOptimizedPropertyFn) { args.add(RValue::get(arg), getContext().getObjCIdType()); args.add(RValue::get(ivarOffset), getContext().getPointerDiffType()); - EmitCall(getTypes().arrangeFreeFunctionCall(getContext().VoidTy, args, - FunctionType::ExtInfo(), - RequiredArgs::All), + EmitCall(getTypes().arrangeBuiltinFunctionCall(getContext().VoidTy, args), setOptimizedPropertyFn, ReturnValueSlot(), args); } else { args.add(RValue::get(ivarOffset), getContext().getPointerDiffType()); @@ -1251,9 +1240,7 @@ CodeGenFunction::generateObjCSetterBody(const ObjCImplementationDecl *classImpl, getContext().BoolTy); // FIXME: We shouldn't need to get the function info here, the runtime // already should have computed it to build the function. - EmitCall(getTypes().arrangeFreeFunctionCall(getContext().VoidTy, args, - FunctionType::ExtInfo(), - RequiredArgs::All), + EmitCall(getTypes().arrangeBuiltinFunctionCall(getContext().VoidTy, args), setPropertyFn, ReturnValueSlot(), args); } @@ -1610,9 +1597,8 @@ void CodeGenFunction::EmitObjCForCollectionStmt(const ObjCForCollectionStmt &S){ Args2.add(RValue::get(V), getContext().getObjCIdType()); // FIXME: We shouldn't need to get the function info here, the runtime already // should have computed it to build the function. - EmitCall(CGM.getTypes().arrangeFreeFunctionCall(getContext().VoidTy, Args2, - FunctionType::ExtInfo(), - RequiredArgs::All), + EmitCall( + CGM.getTypes().arrangeBuiltinFunctionCall(getContext().VoidTy, Args2), EnumerationMutationFn, ReturnValueSlot(), Args2); // Otherwise, or if the mutation function returns, just continue. @@ -3210,8 +3196,8 @@ CodeGenFunction::GenerateObjCAtomicSetterCopyHelperFunction( ImplicitParamDecl srcDecl(getContext(), FD, SourceLocation(), nullptr, SrcTy); args.push_back(&srcDecl); - const CGFunctionInfo &FI = CGM.getTypes().arrangeFreeFunctionDeclaration( - C.VoidTy, args, FunctionType::ExtInfo(), RequiredArgs::All); + const CGFunctionInfo &FI = + CGM.getTypes().arrangeBuiltinFunctionDeclaration(C.VoidTy, args); llvm::FunctionType *LTy = CGM.getTypes().GetFunctionType(FI); @@ -3291,8 +3277,8 @@ CodeGenFunction::GenerateObjCAtomicGetterCopyHelperFunction( ImplicitParamDecl srcDecl(getContext(), FD, SourceLocation(), nullptr, SrcTy); args.push_back(&srcDecl); - const CGFunctionInfo &FI = CGM.getTypes().arrangeFreeFunctionDeclaration( - C.VoidTy, args, FunctionType::ExtInfo(), RequiredArgs::All); + const CGFunctionInfo &FI = + CGM.getTypes().arrangeBuiltinFunctionDeclaration(C.VoidTy, args); llvm::FunctionType *LTy = CGM.getTypes().GetFunctionType(FI); diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index 7678f7b4b20..21f84ca2092 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/lib/CodeGen/CGObjCMac.cpp @@ -244,9 +244,8 @@ class ObjCCommonTypesHelper { Params.push_back(Ctx.getPointerDiffType()->getCanonicalTypeUnqualified()); Params.push_back(Ctx.BoolTy); llvm::FunctionType *FTy = - Types.GetFunctionType(Types.arrangeLLVMFunctionInfo( - IdType, false, false, Params, FunctionType::ExtInfo(), - RequiredArgs::All)); + Types.GetFunctionType( + Types.arrangeBuiltinFunctionDeclaration(IdType, Params)); return CGM.CreateRuntimeFunction(FTy, "objc_getProperty"); } @@ -264,9 +263,8 @@ class ObjCCommonTypesHelper { Params.push_back(Ctx.BoolTy); Params.push_back(Ctx.BoolTy); llvm::FunctionType *FTy = - Types.GetFunctionType(Types.arrangeLLVMFunctionInfo( - Ctx.VoidTy, false, false, Params, FunctionType::ExtInfo(), - RequiredArgs::All)); + Types.GetFunctionType( + Types.arrangeBuiltinFunctionDeclaration(Ctx.VoidTy, Params)); return CGM.CreateRuntimeFunction(FTy, "objc_setProperty"); } @@ -290,9 +288,8 @@ class ObjCCommonTypesHelper { Params.push_back(IdType); Params.push_back(Ctx.getPointerDiffType()->getCanonicalTypeUnqualified()); llvm::FunctionType *FTy = - Types.GetFunctionType(Types.arrangeLLVMFunctionInfo( - Ctx.VoidTy, false, false, Params, FunctionType::ExtInfo(), - RequiredArgs::All)); + Types.GetFunctionType( + Types.arrangeBuiltinFunctionDeclaration(Ctx.VoidTy, Params)); const char *name; if (atomic && copy) name = "objc_setProperty_atomic_copy"; @@ -317,9 +314,8 @@ class ObjCCommonTypesHelper { Params.push_back(Ctx.BoolTy); Params.push_back(Ctx.BoolTy); llvm::FunctionType *FTy = - Types.GetFunctionType(Types.arrangeLLVMFunctionInfo( - Ctx.VoidTy, false, false, Params, FunctionType::ExtInfo(), - RequiredArgs::All)); + Types.GetFunctionType( + Types.arrangeBuiltinFunctionDeclaration(Ctx.VoidTy, Params)); return CGM.CreateRuntimeFunction(FTy, "objc_copyStruct"); } @@ -336,10 +332,8 @@ class ObjCCommonTypesHelper { Params.push_back(Ctx.VoidPtrTy); Params.push_back(Ctx.VoidPtrTy); llvm::FunctionType *FTy = - Types.GetFunctionType(Types.arrangeLLVMFunctionInfo(Ctx.VoidTy, false, false, - Params, - FunctionType::ExtInfo(), - RequiredArgs::All)); + Types.GetFunctionType( + Types.arrangeBuiltinFunctionDeclaration(Ctx.VoidTy, Params)); return CGM.CreateRuntimeFunction(FTy, "objc_copyCppObjectAtomic"); } @@ -350,9 +344,8 @@ class ObjCCommonTypesHelper { SmallVector Params; Params.push_back(Ctx.getCanonicalParamType(Ctx.getObjCIdType())); llvm::FunctionType *FTy = - Types.GetFunctionType(Types.arrangeLLVMFunctionInfo( - Ctx.VoidTy, false, false, Params, FunctionType::ExtInfo(), - RequiredArgs::All)); + Types.GetFunctionType( + Types.arrangeBuiltinFunctionDeclaration(Ctx.VoidTy, Params)); return CGM.CreateRuntimeFunction(FTy, "objc_enumerationMutation"); } diff --git a/lib/CodeGen/CGObjCRuntime.cpp b/lib/CodeGen/CGObjCRuntime.cpp index 01042536a52..0caf6d9f210 100644 --- a/lib/CodeGen/CGObjCRuntime.cpp +++ b/lib/CodeGen/CGObjCRuntime.cpp @@ -363,25 +363,15 @@ CGObjCRuntime::getMessageSendInfo(const ObjCMethodDecl *method, llvm::PointerType *signatureType = CGM.getTypes().GetFunctionType(signature)->getPointerTo(); - // If that's not variadic, there's no need to recompute the ABI - // arrangement. - if (!signature.isVariadic()) - return MessageSendInfo(signature, signatureType); - - // Otherwise, there is. - FunctionType::ExtInfo einfo = signature.getExtInfo(); - const CGFunctionInfo &argsInfo = - CGM.getTypes().arrangeFreeFunctionCall(resultType, callArgs, einfo, - signature.getRequiredArgs()); - - return MessageSendInfo(argsInfo, signatureType); + const CGFunctionInfo &signatureForCall = + CGM.getTypes().arrangeCall(signature, callArgs); + + return MessageSendInfo(signatureForCall, signatureType); } // There's no method; just use a default CC. const CGFunctionInfo &argsInfo = - CGM.getTypes().arrangeFreeFunctionCall(resultType, callArgs, - FunctionType::ExtInfo(), - RequiredArgs::All); + CGM.getTypes().arrangeUnprototypedObjCMessageSend(resultType, callArgs); // Derive the signature to call from that. llvm::PointerType *signatureType = diff --git a/lib/CodeGen/CGOpenMPRuntime.cpp b/lib/CodeGen/CGOpenMPRuntime.cpp index 3b97ba2469a..b0f1009bdb1 100644 --- a/lib/CodeGen/CGOpenMPRuntime.cpp +++ b/lib/CodeGen/CGOpenMPRuntime.cpp @@ -329,6 +329,7 @@ void CGOpenMPRuntime::clear() { InternalVars.clear(); } + // Layout information for ident_t. static CharUnits getIdentAlign(CodeGenModule &CGM) { return CGM.getPointerAlign(); @@ -1144,9 +1145,8 @@ llvm::Function *CGOpenMPRuntime::emitThreadPrivateVarDefinition( /*Id=*/nullptr, CGM.getContext().VoidPtrTy); Args.push_back(&Dst); - auto &FI = CGM.getTypes().arrangeFreeFunctionDeclaration( - CGM.getContext().VoidPtrTy, Args, FunctionType::ExtInfo(), - /*isVariadic=*/false); + auto &FI = CGM.getTypes().arrangeBuiltinFunctionDeclaration( + CGM.getContext().VoidPtrTy, Args); auto FTy = CGM.getTypes().GetFunctionType(FI); auto Fn = CGM.CreateGlobalInitOrDestructFunction( FTy, ".__kmpc_global_ctor_.", FI, Loc); @@ -1176,9 +1176,8 @@ llvm::Function *CGOpenMPRuntime::emitThreadPrivateVarDefinition( /*Id=*/nullptr, CGM.getContext().VoidPtrTy); Args.push_back(&Dst); - auto &FI = CGM.getTypes().arrangeFreeFunctionDeclaration( - CGM.getContext().VoidTy, Args, FunctionType::ExtInfo(), - /*isVariadic=*/false); + auto &FI = CGM.getTypes().arrangeBuiltinFunctionDeclaration( + CGM.getContext().VoidTy, Args); auto FTy = CGM.getTypes().GetFunctionType(FI); auto Fn = CGM.CreateGlobalInitOrDestructFunction( FTy, ".__kmpc_global_dtor_.", FI, Loc); @@ -1549,9 +1548,7 @@ static llvm::Value *emitCopyprivateCopyFunction( C.VoidPtrTy); Args.push_back(&LHSArg); Args.push_back(&RHSArg); - FunctionType::ExtInfo EI; - auto &CGFI = CGM.getTypes().arrangeFreeFunctionDeclaration( - C.VoidTy, Args, EI, /*isVariadic=*/false); + auto &CGFI = CGM.getTypes().arrangeBuiltinFunctionDeclaration(C.VoidTy, Args); auto *Fn = llvm::Function::Create( CGM.getTypes().GetFunctionType(CGFI), llvm::GlobalValue::InternalLinkage, ".omp.copyprivate.copy_func", &CGM.getModule()); @@ -2092,9 +2089,7 @@ createOffloadingBinaryDescriptorFunction(CodeGenModule &CGM, StringRef Name, CodeGenFunction CGF(CGM); GlobalDecl(); - auto &FI = CGM.getTypes().arrangeFreeFunctionDeclaration( - C.VoidTy, Args, FunctionType::ExtInfo(), - /*isVariadic=*/false); + auto &FI = CGM.getTypes().arrangeBuiltinFunctionDeclaration(C.VoidTy, Args); auto FTy = CGM.getTypes().GetFunctionType(FI); auto *Fn = CGM.CreateGlobalInitOrDestructFunction(FTy, Name, FI, SourceLocation()); @@ -2574,10 +2569,8 @@ emitProxyTaskFunction(CodeGenModule &CGM, SourceLocation Loc, KmpTaskTWithPrivatesPtrQTy.withRestrict()); Args.push_back(&GtidArg); Args.push_back(&TaskTypeArg); - FunctionType::ExtInfo Info; auto &TaskEntryFnInfo = - CGM.getTypes().arrangeFreeFunctionDeclaration(KmpInt32Ty, Args, Info, - /*isVariadic=*/false); + CGM.getTypes().arrangeBuiltinFunctionDeclaration(KmpInt32Ty, Args); auto *TaskEntryTy = CGM.getTypes().GetFunctionType(TaskEntryFnInfo); auto *TaskEntry = llvm::Function::Create(TaskEntryTy, llvm::GlobalValue::InternalLinkage, @@ -2643,8 +2636,7 @@ static llvm::Value *emitDestructorsFunction(CodeGenModule &CGM, Args.push_back(&TaskTypeArg); FunctionType::ExtInfo Info; auto &DestructorFnInfo = - CGM.getTypes().arrangeFreeFunctionDeclaration(KmpInt32Ty, Args, Info, - /*isVariadic=*/false); + CGM.getTypes().arrangeBuiltinFunctionDeclaration(KmpInt32Ty, Args); auto *DestructorFnTy = CGM.getTypes().GetFunctionType(DestructorFnInfo); auto *DestructorFn = llvm::Function::Create(DestructorFnTy, llvm::GlobalValue::InternalLinkage, @@ -2717,10 +2709,8 @@ emitTaskPrivateMappingFunction(CodeGenModule &CGM, SourceLocation Loc, PrivateVarsPos[VD] = Counter; ++Counter; } - FunctionType::ExtInfo Info; auto &TaskPrivatesMapFnInfo = - CGM.getTypes().arrangeFreeFunctionDeclaration(C.VoidTy, Args, Info, - /*isVariadic=*/false); + CGM.getTypes().arrangeBuiltinFunctionDeclaration(C.VoidTy, Args); auto *TaskPrivatesMapTy = CGM.getTypes().GetFunctionType(TaskPrivatesMapFnInfo); auto *TaskPrivatesMap = llvm::Function::Create( @@ -3225,9 +3215,7 @@ static llvm::Value *emitReductionFunction(CodeGenModule &CGM, C.VoidPtrTy); Args.push_back(&LHSArg); Args.push_back(&RHSArg); - FunctionType::ExtInfo EI; - auto &CGFI = CGM.getTypes().arrangeFreeFunctionDeclaration( - C.VoidTy, Args, EI, /*isVariadic=*/false); + auto &CGFI = CGM.getTypes().arrangeBuiltinFunctionDeclaration(C.VoidTy, Args); auto *Fn = llvm::Function::Create( CGM.getTypes().GetFunctionType(CGFI), llvm::GlobalValue::InternalLinkage, ".omp.reduction.reduction_func", &CGM.getModule()); diff --git a/lib/CodeGen/CGStmt.cpp b/lib/CodeGen/CGStmt.cpp index cc4fa2ec597..a1e44c807e5 100644 --- a/lib/CodeGen/CGStmt.cpp +++ b/lib/CodeGen/CGStmt.cpp @@ -2147,8 +2147,7 @@ CodeGenFunction::GenerateCapturedStmtFunction(const CapturedStmt &S) { // Create the function declaration. FunctionType::ExtInfo ExtInfo; const CGFunctionInfo &FuncInfo = - CGM.getTypes().arrangeFreeFunctionDeclaration(Ctx.VoidTy, Args, ExtInfo, - /*IsVariadic=*/false); + CGM.getTypes().arrangeBuiltinFunctionDeclaration(Ctx.VoidTy, Args); llvm::FunctionType *FuncLLVMTy = CGM.getTypes().GetFunctionType(FuncInfo); llvm::Function *F = diff --git a/lib/CodeGen/CGStmtOpenMP.cpp b/lib/CodeGen/CGStmtOpenMP.cpp index bf00b04563c..68bd68bbf12 100644 --- a/lib/CodeGen/CGStmtOpenMP.cpp +++ b/lib/CodeGen/CGStmtOpenMP.cpp @@ -120,8 +120,7 @@ CodeGenFunction::GenerateOpenMPCapturedStmtFunction(const CapturedStmt &S) { // Create the function declaration. FunctionType::ExtInfo ExtInfo; const CGFunctionInfo &FuncInfo = - CGM.getTypes().arrangeFreeFunctionDeclaration(Ctx.VoidTy, Args, ExtInfo, - /*IsVariadic=*/false); + CGM.getTypes().arrangeBuiltinFunctionDeclaration(Ctx.VoidTy, Args); llvm::FunctionType *FuncLLVMTy = CGM.getTypes().GetFunctionType(FuncInfo); llvm::Function *F = llvm::Function::Create( diff --git a/lib/CodeGen/CodeGenABITypes.cpp b/lib/CodeGen/CodeGenABITypes.cpp index 643c996e2ec..16a7db47c80 100644 --- a/lib/CodeGen/CodeGenABITypes.cpp +++ b/lib/CodeGen/CodeGenABITypes.cpp @@ -66,5 +66,5 @@ const CGFunctionInfo &CodeGenABITypes::arrangeFreeFunctionCall( FunctionType::ExtInfo info, RequiredArgs args) { return CGM->getTypes().arrangeLLVMFunctionInfo( returnType, /*IsInstanceMethod=*/false, /*IsChainCall=*/false, argTypes, - info, args); + info, {}, args); } diff --git a/lib/CodeGen/CodeGenTypes.h b/lib/CodeGen/CodeGenTypes.h index a96f23c4489..477ea602bd3 100644 --- a/lib/CodeGen/CodeGenTypes.h +++ b/lib/CodeGen/CodeGenTypes.h @@ -214,6 +214,10 @@ class CodeGenTypes { /// replace the 'opaque' type we previously made for it if applicable. void UpdateCompletedType(const TagDecl *TD); + /// \brief Remove stale types from the type cache when an inheritance model + /// gets assigned to a class. + void RefreshTypeCacheForClass(const CXXRecordDecl *RD); + /// getNullaryFunctionInfo - Get the function info for a void() /// function with standard CC. const CGFunctionInfo &arrangeNullaryFunction(); @@ -239,16 +243,55 @@ class CodeGenTypes { // this for compatibility reasons. const CGFunctionInfo &arrangeGlobalDeclaration(GlobalDecl GD); + + /// Given a function info for a declaration, return the function info + /// for a call with the given arguments. + /// + /// Often this will be able to simply return the declaration info. + const CGFunctionInfo &arrangeCall(const CGFunctionInfo &declFI, + const CallArgList &args); + + /// Free functions are functions that are compatible with an ordinary + /// C function pointer type. const CGFunctionInfo &arrangeFunctionDeclaration(const FunctionDecl *FD); + const CGFunctionInfo &arrangeFreeFunctionCall(const CallArgList &Args, + const FunctionType *Ty, + bool ChainCall); + const CGFunctionInfo &arrangeFreeFunctionType(CanQual Ty, + const FunctionDecl *FD); + const CGFunctionInfo &arrangeFreeFunctionType(CanQual Ty); + + /// A nullary function is a freestanding function of type 'void ()'. + /// This method works for both calls and declarations. + const CGFunctionInfo &arrangeNullaryFunction(); + + /// A builtin function is a freestanding function using the default + /// C conventions. const CGFunctionInfo & - arrangeFreeFunctionDeclaration(QualType ResTy, const FunctionArgList &Args, - const FunctionType::ExtInfo &Info, - bool isVariadic); + arrangeBuiltinFunctionDeclaration(QualType resultType, + const FunctionArgList &args); + const CGFunctionInfo & + arrangeBuiltinFunctionDeclaration(CanQualType resultType, + ArrayRef argTypes); + const CGFunctionInfo &arrangeBuiltinFunctionCall(QualType resultType, + const CallArgList &args); + /// Objective-C methods are C functions with some implicit parameters. const CGFunctionInfo &arrangeObjCMethodDeclaration(const ObjCMethodDecl *MD); const CGFunctionInfo &arrangeObjCMessageSendSignature(const ObjCMethodDecl *MD, QualType receiverType); + const CGFunctionInfo &arrangeUnprototypedObjCMessageSend( + QualType returnType, + const CallArgList &args); + + /// Block invocation functions are C functions with an implicit parameter. + const CGFunctionInfo &arrangeBlockFunctionDeclaration( + const FunctionProtoType *type, + const FunctionArgList &args); + const CGFunctionInfo &arrangeBlockFunctionCall(const CallArgList &args, + const FunctionType *type); + /// C++ methods have some special rules and also have implicit parameters. const CGFunctionInfo &arrangeCXXMethodDeclaration(const CXXMethodDecl *MD); const CGFunctionInfo &arrangeCXXStructorDeclaration(const CXXMethodDecl *MD, StructorType Type); @@ -256,15 +299,6 @@ class CodeGenTypes { const CXXConstructorDecl *D, CXXCtorType CtorKind, unsigned ExtraArgs); - const CGFunctionInfo &arrangeFreeFunctionCall(const CallArgList &Args, - const FunctionType *Ty, - bool ChainCall); - const CGFunctionInfo &arrangeFreeFunctionCall(QualType ResTy, - const CallArgList &args, - FunctionType::ExtInfo info, - RequiredArgs required); - const CGFunctionInfo &arrangeBlockFunctionCall(const CallArgList &args, - const FunctionType *type); const CGFunctionInfo &arrangeCXXMethodCall(const CallArgList &args, const FunctionProtoType *type, @@ -272,9 +306,6 @@ class CodeGenTypes { const CGFunctionInfo &arrangeMSMemberPointerThunk(const CXXMethodDecl *MD); const CGFunctionInfo &arrangeMSCtorClosure(const CXXConstructorDecl *CD, CXXCtorType CT); - const CGFunctionInfo &arrangeFreeFunctionType(CanQual Ty, - const FunctionDecl *FD); - const CGFunctionInfo &arrangeFreeFunctionType(CanQual Ty); const CGFunctionInfo &arrangeCXXMethodType(const CXXRecordDecl *RD, const FunctionProtoType *FTP, const CXXMethodDecl *MD); @@ -290,6 +321,7 @@ class CodeGenTypes { bool chainCall, ArrayRef argTypes, FunctionType::ExtInfo info, + ArrayRef paramInfos, RequiredArgs args); /// \brief Compute a new LLVM record layout object for the given record. diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp index 8b6322a4b75..8fe6cf457e2 100644 --- a/lib/CodeGen/ItaniumCXXABI.cpp +++ b/lib/CodeGen/ItaniumCXXABI.cpp @@ -2187,9 +2187,8 @@ ItaniumCXXABI::getOrCreateThreadLocalWrapper(const VarDecl *VD, if (RetQT->isReferenceType()) RetQT = RetQT.getNonReferenceType(); - const CGFunctionInfo &FI = CGM.getTypes().arrangeFreeFunctionDeclaration( - getContext().getPointerType(RetQT), FunctionArgList(), - FunctionType::ExtInfo(), false); + const CGFunctionInfo &FI = CGM.getTypes().arrangeBuiltinFunctionDeclaration( + getContext().getPointerType(RetQT), FunctionArgList()); llvm::FunctionType *FnTy = CGM.getTypes().GetFunctionType(FI); llvm::Function *Wrapper = @@ -2276,9 +2275,7 @@ void ItaniumCXXABI::EmitThreadLocalInitFuncs( Init = llvm::Function::Create( FnTy, llvm::GlobalVariable::ExternalWeakLinkage, InitFnName.str(), &CGM.getModule()); - const CGFunctionInfo &FI = CGM.getTypes().arrangeFreeFunctionDeclaration( - CGM.getContext().VoidTy, FunctionArgList(), FunctionType::ExtInfo(), - false); + const CGFunctionInfo &FI = CGM.getTypes().arrangeNullaryFunction(); CGM.SetLLVMFunctionAttributes(nullptr, FI, cast(Init)); } From b397291cd5b84d09a0e73ec4209e63a19fe58f79 Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 11 Mar 2016 04:30:43 +0000 Subject: [PATCH 425/742] Add a coerce-and-expand ABIArgInfo as a generalization of some of the things we do with Expand / Direct. NFC for now, but this will be used by swiftcall expansion. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263192 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/CodeGen/CGFunctionInfo.h | 121 ++++++++++++++++++++--- lib/CodeGen/CGBuilder.h | 7 ++ lib/CodeGen/CGCall.cpp | 127 ++++++++++++++++++++++++- lib/CodeGen/TargetInfo.cpp | 7 ++ 4 files changed, 243 insertions(+), 19 deletions(-) diff --git a/include/clang/CodeGen/CGFunctionInfo.h b/include/clang/CodeGen/CGFunctionInfo.h index 9af28975bdd..5066c5dfda3 100644 --- a/include/clang/CodeGen/CGFunctionInfo.h +++ b/include/clang/CodeGen/CGFunctionInfo.h @@ -19,14 +19,10 @@ #include "clang/AST/CanonicalType.h" #include "clang/AST/CharUnits.h" #include "clang/AST/Type.h" +#include "llvm/IR/DerivedTypes.h" #include "llvm/ADT/FoldingSet.h" #include -namespace llvm { - class Type; - class StructType; -} - namespace clang { class Decl; @@ -63,6 +59,12 @@ class ABIArgInfo { /// are all scalar types or are themselves expandable types. Expand, + /// CoerceAndExpand - Only valid for aggregate argument types. The + /// structure should be expanded into consecutive arguments corresponding + /// to the non-array elements of the type stored in CoerceToType. + /// Array elements in the type are assumed to be padding and skipped. + CoerceAndExpand, + /// InAlloca - Pass the argument directly using the LLVM inalloca attribute. /// This is similar to indirect with byval, except it only applies to /// arguments stored in memory and forbids any implicit copies. When @@ -74,8 +76,11 @@ class ABIArgInfo { }; private: - llvm::Type *TypeData; // isDirect() || isExtend() - llvm::Type *PaddingType; + llvm::Type *TypeData; // canHaveCoerceToType() + union { + llvm::Type *PaddingType; // canHavePaddingType() + llvm::Type *UnpaddedCoerceAndExpandType; // isCoerceAndExpand() + }; union { unsigned DirectOffset; // isDirect() || isExtend() unsigned IndirectAlign; // isIndirect() @@ -90,8 +95,22 @@ class ABIArgInfo { bool InReg : 1; // isDirect() || isExtend() || isIndirect() bool CanBeFlattened: 1; // isDirect() + bool canHavePaddingType() const { + return isDirect() || isExtend() || isIndirect() || isExpand(); + } + void setPaddingType(llvm::Type *T) { + assert(canHavePaddingType()); + PaddingType = T; + } + + void setUnpaddedCoerceToType(llvm::Type *T) { + assert(isCoerceAndExpand()); + UnpaddedCoerceAndExpandType = T; + } + ABIArgInfo(Kind K) - : PaddingType(nullptr), TheKind(K), PaddingInReg(false), InReg(false) {} + : TheKind(K), PaddingInReg(false), InReg(false) { + } public: ABIArgInfo() @@ -103,8 +122,8 @@ class ABIArgInfo { bool CanBeFlattened = true) { auto AI = ABIArgInfo(Direct); AI.setCoerceToType(T); - AI.setDirectOffset(Offset); AI.setPaddingType(Padding); + AI.setDirectOffset(Offset); AI.setCanBeFlattened(CanBeFlattened); return AI; } @@ -116,6 +135,7 @@ class ABIArgInfo { static ABIArgInfo getExtend(llvm::Type *T = nullptr) { auto AI = ABIArgInfo(Extend); AI.setCoerceToType(T); + AI.setPaddingType(nullptr); AI.setDirectOffset(0); return AI; } @@ -150,7 +170,9 @@ class ABIArgInfo { return AI; } static ABIArgInfo getExpand() { - return ABIArgInfo(Expand); + auto AI = ABIArgInfo(Expand); + AI.setPaddingType(nullptr); + return AI; } static ABIArgInfo getExpandWithPadding(bool PaddingInReg, llvm::Type *Padding) { @@ -160,6 +182,54 @@ class ABIArgInfo { return AI; } + /// \param unpaddedCoerceToType The coerce-to type with padding elements + /// removed, canonicalized to a single element if it would otherwise + /// have exactly one element. + static ABIArgInfo getCoerceAndExpand(llvm::StructType *coerceToType, + llvm::Type *unpaddedCoerceToType) { +#ifndef NDEBUG + // Sanity checks on unpaddedCoerceToType. + + // Assert that we only have a struct type if there are multiple elements. + auto unpaddedStruct = dyn_cast(unpaddedCoerceToType); + assert(!unpaddedStruct || unpaddedStruct->getNumElements() != 1); + + // Assert that all the non-padding elements have a corresponding element + // in the unpadded type. + unsigned unpaddedIndex = 0; + for (auto eltType : coerceToType->elements()) { + if (isPaddingForCoerceAndExpand(eltType)) continue; + if (unpaddedStruct) { + assert(unpaddedStruct->getElementType(unpaddedIndex) == eltType); + } else { + assert(unpaddedIndex == 0 && unpaddedCoerceToType == eltType); + } + unpaddedIndex++; + } + + // Assert that there aren't extra elements in the unpadded type. + if (unpaddedStruct) { + assert(unpaddedStruct->getNumElements() == unpaddedIndex); + } else { + assert(unpaddedIndex == 1); + } +#endif + + auto AI = ABIArgInfo(CoerceAndExpand); + AI.setCoerceToType(coerceToType); + AI.setUnpaddedCoerceToType(unpaddedCoerceToType); + return AI; + } + + static bool isPaddingForCoerceAndExpand(llvm::Type *eltType) { + if (eltType->isArrayTy()) { + assert(eltType->getArrayElementType()->isIntegerTy(8)); + return true; + } else { + return false; + } + } + Kind getKind() const { return TheKind; } bool isDirect() const { return TheKind == Direct; } bool isInAlloca() const { return TheKind == InAlloca; } @@ -167,8 +237,11 @@ class ABIArgInfo { bool isIgnore() const { return TheKind == Ignore; } bool isIndirect() const { return TheKind == Indirect; } bool isExpand() const { return TheKind == Expand; } + bool isCoerceAndExpand() const { return TheKind == CoerceAndExpand; } - bool canHaveCoerceToType() const { return isDirect() || isExtend(); } + bool canHaveCoerceToType() const { + return isDirect() || isExtend() || isCoerceAndExpand(); + } // Direct/Extend accessors unsigned getDirectOffset() const { @@ -180,9 +253,9 @@ class ABIArgInfo { DirectOffset = Offset; } - llvm::Type *getPaddingType() const { return PaddingType; } - - void setPaddingType(llvm::Type *T) { PaddingType = T; } + llvm::Type *getPaddingType() const { + return (canHavePaddingType() ? PaddingType : nullptr); + } bool getPaddingInReg() const { return PaddingInReg; @@ -201,6 +274,26 @@ class ABIArgInfo { TypeData = T; } + llvm::StructType *getCoerceAndExpandType() const { + assert(isCoerceAndExpand()); + return cast(TypeData); + } + + llvm::Type *getUnpaddedCoerceAndExpandType() const { + assert(isCoerceAndExpand()); + return UnpaddedCoerceAndExpandType; + } + + ArrayRefgetCoerceAndExpandTypeSequence() const { + assert(isCoerceAndExpand()); + if (auto structTy = + dyn_cast(UnpaddedCoerceAndExpandType)) { + return structTy->elements(); + } else { + return llvm::makeArrayRef(&UnpaddedCoerceAndExpandType, 1); + } + } + bool getInReg() const { assert((isDirect() || isExtend() || isIndirect()) && "Invalid kind!"); return InReg; diff --git a/lib/CodeGen/CGBuilder.h b/lib/CodeGen/CGBuilder.h index 489f3413d4b..baba30d5bf6 100644 --- a/lib/CodeGen/CGBuilder.h +++ b/lib/CodeGen/CGBuilder.h @@ -10,6 +10,7 @@ #ifndef LLVM_CLANG_LIB_CODEGEN_CGBUILDER_H #define LLVM_CLANG_LIB_CODEGEN_CGBUILDER_H +#include "llvm/IR/DataLayout.h" #include "llvm/IR/IRBuilder.h" #include "Address.h" #include "CodeGenTypeCache.h" @@ -194,6 +195,12 @@ class CGBuilderTy : public CGBuilderBaseTy { Addr.getPointer(), Index, Name), Addr.getAlignment().alignmentAtOffset(Offset)); } + Address CreateStructGEP(Address Addr, unsigned Index, + const llvm::StructLayout *Layout, + const llvm::Twine &Name = "") { + auto Offset = CharUnits::fromQuantity(Layout->getElementOffset(Index)); + return CreateStructGEP(Addr, Index, Offset, Name); + } /// Given /// %addr = [n x T]* ... diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index 8a4838945b1..3711d83a92c 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -1363,11 +1363,13 @@ void ClangToLLVMArgMapping::construct(const ASTContext &Context, // ignore and inalloca doesn't have matching LLVM parameters. IRArgs.NumberOfArgs = 0; break; - case ABIArgInfo::Expand: { + case ABIArgInfo::CoerceAndExpand: + IRArgs.NumberOfArgs = AI.getCoerceAndExpandTypeSequence().size(); + break; + case ABIArgInfo::Expand: IRArgs.NumberOfArgs = getExpansionSize(ArgType, Context); break; } - } if (IRArgs.NumberOfArgs > 0) { IRArgs.FirstArgIndex = IRArgNo; @@ -1466,6 +1468,10 @@ CodeGenTypes::GetFunctionType(const CGFunctionInfo &FI) { case ABIArgInfo::Ignore: resultType = llvm::Type::getVoidTy(getLLVMContext()); break; + + case ABIArgInfo::CoerceAndExpand: + resultType = retAI.getUnpaddedCoerceAndExpandType(); + break; } ClangToLLVMArgMapping IRFunctionArgs(getContext(), FI, true); @@ -1533,6 +1539,15 @@ CodeGenTypes::GetFunctionType(const CGFunctionInfo &FI) { break; } + case ABIArgInfo::CoerceAndExpand: { + auto ArgTypesIter = ArgTypes.begin() + FirstIRArg; + for (auto EltTy : ArgInfo.getCoerceAndExpandTypeSequence()) { + *ArgTypesIter++ = EltTy; + } + assert(ArgTypesIter == ArgTypes.begin() + FirstIRArg + NumIRArgs); + break; + } + case ABIArgInfo::Expand: auto ArgTypesIter = ArgTypes.begin() + FirstIRArg; getExpandedTypes(it->type, ArgTypesIter); @@ -1763,6 +1778,9 @@ void CodeGenModule::ConstructAttributeList( break; } + case ABIArgInfo::CoerceAndExpand: + break; + case ABIArgInfo::Expand: llvm_unreachable("Invalid ABI kind for return argument"); } @@ -1870,7 +1888,8 @@ void CodeGenModule::ConstructAttributeList( } case ABIArgInfo::Ignore: case ABIArgInfo::Expand: - continue; + case ABIArgInfo::CoerceAndExpand: + break; case ABIArgInfo::InAlloca: // inalloca disables readnone and readonly. @@ -2243,6 +2262,29 @@ void CodeGenFunction::EmitFunctionProlog(const CGFunctionInfo &FI, break; } + case ABIArgInfo::CoerceAndExpand: { + // Reconstruct into a temporary. + Address alloca = CreateMemTemp(Ty, getContext().getDeclAlign(Arg)); + ArgVals.push_back(ParamValue::forIndirect(alloca)); + + auto coercionType = ArgI.getCoerceAndExpandType(); + alloca = Builder.CreateElementBitCast(alloca, coercionType); + auto layout = CGM.getDataLayout().getStructLayout(coercionType); + + unsigned argIndex = FirstIRArg; + for (unsigned i = 0, e = coercionType->getNumElements(); i != e; ++i) { + llvm::Type *eltType = coercionType->getElementType(i); + if (ABIArgInfo::isPaddingForCoerceAndExpand(eltType)) + continue; + + auto eltAddr = Builder.CreateStructGEP(alloca, i, layout); + auto elt = FnArgs[argIndex++]; + Builder.CreateStore(elt, eltAddr); + } + assert(argIndex == FirstIRArg + NumIRArgs); + break; + } + case ABIArgInfo::Expand: { // If this structure was expanded into multiple arguments then // we need to create a temporary and reconstruct it from the @@ -2616,6 +2658,40 @@ void CodeGenFunction::EmitFunctionEpilog(const CGFunctionInfo &FI, case ABIArgInfo::Ignore: break; + case ABIArgInfo::CoerceAndExpand: { + auto coercionType = RetAI.getCoerceAndExpandType(); + auto layout = CGM.getDataLayout().getStructLayout(coercionType); + + // Load all of the coerced elements out into results. + llvm::SmallVector results; + Address addr = Builder.CreateElementBitCast(ReturnValue, coercionType); + for (unsigned i = 0, e = coercionType->getNumElements(); i != e; ++i) { + auto coercedEltType = coercionType->getElementType(i); + if (ABIArgInfo::isPaddingForCoerceAndExpand(coercedEltType)) + continue; + + auto eltAddr = Builder.CreateStructGEP(addr, i, layout); + auto elt = Builder.CreateLoad(eltAddr); + results.push_back(elt); + } + + // If we have one result, it's the single direct result type. + if (results.size() == 1) { + RV = results[0]; + + // Otherwise, we need to make a first-class aggregate. + } else { + // Construct a return type that lacks padding elements. + llvm::Type *returnType = RetAI.getUnpaddedCoerceAndExpandType(); + + RV = llvm::UndefValue::get(returnType); + for (unsigned i = 0, e = results.size(); i != e; ++i) { + RV = Builder.CreateInsertValue(RV, results[i], i); + } + } + break; + } + case ABIArgInfo::Expand: llvm_unreachable("Invalid ABI kind for return argument"); } @@ -3349,7 +3425,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, // alloca to hold the result, unless one is given to us. Address SRetPtr = Address::invalid(); size_t UnusedReturnSize = 0; - if (RetAI.isIndirect() || RetAI.isInAlloca()) { + if (RetAI.isIndirect() || RetAI.isInAlloca() || RetAI.isCoerceAndExpand()) { if (!ReturnValue.isNull()) { SRetPtr = ReturnValue.getValue(); } else { @@ -3363,7 +3439,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, } if (IRFunctionArgs.hasSRetArg()) { IRCallArgs[IRFunctionArgs.getSRetArgNo()] = SRetPtr.getPointer(); - } else { + } else if (RetAI.isInAlloca()) { Address Addr = createInAllocaStructGEP(RetAI.getInAllocaFieldIndex()); Builder.CreateStore(SRetPtr.getPointer(), Addr); } @@ -3543,6 +3619,29 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, break; } + case ABIArgInfo::CoerceAndExpand: { + assert(RV.isAggregate() && + "CoerceAndExpand does not support non-aggregate types yet"); + + auto coercionType = ArgInfo.getCoerceAndExpandType(); + auto layout = CGM.getDataLayout().getStructLayout(coercionType); + + Address addr = RV.getAggregateAddress(); + addr = Builder.CreateElementBitCast(addr, coercionType); + + unsigned IRArgPos = FirstIRArg; + for (unsigned i = 0, e = coercionType->getNumElements(); i != e; ++i) { + llvm::Type *eltType = coercionType->getElementType(i); + if (ABIArgInfo::isPaddingForCoerceAndExpand(eltType)) continue; + Address eltAddr = Builder.CreateStructGEP(addr, i, layout); + llvm::Value *elt = Builder.CreateLoad(eltAddr); + IRCallArgs[IRArgPos++] = elt; + } + assert(IRArgPos == FirstIRArg + NumIRArgs); + + break; + } + case ABIArgInfo::Expand: unsigned IRArgPos = FirstIRArg; ExpandTypeToArgs(I->Ty, RV, IRFuncTy, IRCallArgs, IRArgPos); @@ -3737,6 +3836,24 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, return ret; } + case ABIArgInfo::CoerceAndExpand: { + auto coercionType = RetAI.getCoerceAndExpandType(); + auto layout = CGM.getDataLayout().getStructLayout(coercionType); + + Address addr = SRetPtr; + addr = Builder.CreateElementBitCast(addr, coercionType); + + unsigned unpaddedIndex = 0; + for (unsigned i = 0, e = coercionType->getNumElements(); i != e; ++i) { + llvm::Type *eltType = coercionType->getElementType(i); + if (ABIArgInfo::isPaddingForCoerceAndExpand(eltType)) continue; + Address eltAddr = Builder.CreateStructGEP(addr, i, layout); + llvm::Value *elt = Builder.CreateExtractValue(CI, unpaddedIndex++); + Builder.CreateStore(elt, eltAddr); + } + break; + } + case ABIArgInfo::Ignore: // If we are ignoring an argument that had a result, make sure to // construct the appropriate return value for our caller. diff --git a/lib/CodeGen/TargetInfo.cpp b/lib/CodeGen/TargetInfo.cpp index cdb0a6a168b..dc3bf33f0ec 100644 --- a/lib/CodeGen/TargetInfo.cpp +++ b/lib/CodeGen/TargetInfo.cpp @@ -158,6 +158,10 @@ void ABIArgInfo::dump() const { case Expand: OS << "Expand"; break; + case CoerceAndExpand: + OS << "CoerceAndExpand Type="; + getCoerceAndExpandType()->print(OS); + break; } OS << ")\n"; } @@ -1501,6 +1505,7 @@ static bool isArgInAlloca(const ABIArgInfo &Info) { case ABIArgInfo::Direct: case ABIArgInfo::Extend: case ABIArgInfo::Expand: + case ABIArgInfo::CoerceAndExpand: if (Info.getInReg()) return false; return true; @@ -6754,6 +6759,7 @@ Address SparcV9ABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, CharUnits Stride; switch (AI.getKind()) { case ABIArgInfo::Expand: + case ABIArgInfo::CoerceAndExpand: case ABIArgInfo::InAlloca: llvm_unreachable("Unsupported ABI kind for va_arg"); @@ -6982,6 +6988,7 @@ Address XCoreABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, CharUnits ArgSize = CharUnits::Zero(); switch (AI.getKind()) { case ABIArgInfo::Expand: + case ABIArgInfo::CoerceAndExpand: case ABIArgInfo::InAlloca: llvm_unreachable("Unsupported ABI kind for va_arg"); case ABIArgInfo::Ignore: From f39ea145f3a1ec5ab9d7fec584f3750cd68b8a34 Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 11 Mar 2016 04:55:21 +0000 Subject: [PATCH 426/742] Speculatively attempt to fix the MSVC build by making some methods non-private. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263193 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/CodeGen/CGFunctionInfo.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/clang/CodeGen/CGFunctionInfo.h b/include/clang/CodeGen/CGFunctionInfo.h index 5066c5dfda3..b8f67d52183 100644 --- a/include/clang/CodeGen/CGFunctionInfo.h +++ b/include/clang/CodeGen/CGFunctionInfo.h @@ -496,6 +496,15 @@ class CGFunctionInfo : public llvm::FoldingSetNode { ArrayRef argTypes, RequiredArgs required); + // Friending class TrailingObjects is apparently not good enough for MSVC, + // so these have to be public. + size_t numTrailingObjects(OverloadToken) const { + return NumArgs + 1; + } + size_t numTrailingObjects(OverloadToken) const { + return (HasExtParameterInfos ? NumArgs : 0); + } + typedef const ArgInfo *const_arg_iterator; typedef ArgInfo *arg_iterator; From 421d0a75948424fb1b203141a94147db4fb33d84 Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 11 Mar 2016 05:03:01 +0000 Subject: [PATCH 427/742] Removing the friend declaration was not a good idea. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263194 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/CodeGen/CGFunctionInfo.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/clang/CodeGen/CGFunctionInfo.h b/include/clang/CodeGen/CGFunctionInfo.h index b8f67d52183..decc8073ca5 100644 --- a/include/clang/CodeGen/CGFunctionInfo.h +++ b/include/clang/CodeGen/CGFunctionInfo.h @@ -498,6 +498,7 @@ class CGFunctionInfo : public llvm::FoldingSetNode { // Friending class TrailingObjects is apparently not good enough for MSVC, // so these have to be public. + friend class TrailingObjects; size_t numTrailingObjects(OverloadToken) const { return NumArgs + 1; } From 0b81a63e98770bf26763d9ccc86c44c45527142f Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 4 Apr 2016 13:10:14 -0700 Subject: [PATCH 428/742] Fix cherry-pick for this branch. --- include/clang/CodeGen/CGFunctionInfo.h | 10 ---------- lib/CodeGen/CGObjCMac.cpp | 7 +++---- lib/CodeGen/CodeGenTypes.h | 4 ---- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/include/clang/CodeGen/CGFunctionInfo.h b/include/clang/CodeGen/CGFunctionInfo.h index decc8073ca5..5066c5dfda3 100644 --- a/include/clang/CodeGen/CGFunctionInfo.h +++ b/include/clang/CodeGen/CGFunctionInfo.h @@ -496,16 +496,6 @@ class CGFunctionInfo : public llvm::FoldingSetNode { ArrayRef argTypes, RequiredArgs required); - // Friending class TrailingObjects is apparently not good enough for MSVC, - // so these have to be public. - friend class TrailingObjects; - size_t numTrailingObjects(OverloadToken) const { - return NumArgs + 1; - } - size_t numTrailingObjects(OverloadToken) const { - return (HasExtParameterInfos ? NumArgs : 0); - } - typedef const ArgInfo *const_arg_iterator; typedef ArgInfo *arg_iterator; diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index 21f84ca2092..7488f28ca17 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/lib/CodeGen/CGObjCMac.cpp @@ -357,11 +357,10 @@ class ObjCCommonTypesHelper { Params.push_back( Ctx.getCanonicalType(Ctx.getPointerType(Ctx.CharTy.withConst()))); llvm::FunctionType *FTy = - Types.GetFunctionType(Types.arrangeLLVMFunctionInfo( + Types.GetFunctionType( + Types.arrangeBuiltinFunctionDeclaration( Ctx.getCanonicalType(Ctx.getObjCClassType()), - false, false, Params, - FunctionType::ExtInfo(), - RequiredArgs::All)); + Params)); return CGM.CreateRuntimeFunction(FTy, "objc_lookUpClass"); } diff --git a/lib/CodeGen/CodeGenTypes.h b/lib/CodeGen/CodeGenTypes.h index 477ea602bd3..34f6b611365 100644 --- a/lib/CodeGen/CodeGenTypes.h +++ b/lib/CodeGen/CodeGenTypes.h @@ -218,10 +218,6 @@ class CodeGenTypes { /// gets assigned to a class. void RefreshTypeCacheForClass(const CXXRecordDecl *RD); - /// getNullaryFunctionInfo - Get the function info for a void() - /// function with standard CC. - const CGFunctionInfo &arrangeNullaryFunction(); - // The arrangement methods are split into three families: // - those meant to drive the signature and prologue/epilogue // of a function declaration or definition, From 102b1d545b872cc618669ab5b744c058cb7c8312 Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 4 Apr 2016 18:33:00 +0000 Subject: [PATCH 429/742] Add a couple of convenience operations to CharUnits. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265323 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/CharUnits.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/clang/AST/CharUnits.h b/include/clang/AST/CharUnits.h index 3a819d5ed63..b2da51c23b8 100644 --- a/include/clang/AST/CharUnits.h +++ b/include/clang/AST/CharUnits.h @@ -142,9 +142,17 @@ namespace clang { CharUnits operator* (QuantityType N) const { return CharUnits(Quantity * N); } + CharUnits &operator*= (QuantityType N) { + Quantity *= N; + return *this; + } CharUnits operator/ (QuantityType N) const { return CharUnits(Quantity / N); } + CharUnits operator/= (QuantityType N) { + Quantity /= N; + return *this; + } QuantityType operator/ (const CharUnits &Other) const { return Quantity / Other.Quantity; } From ae5a19a014fe6cf383edf2dd70b3df972feab6fd Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 4 Apr 2016 18:33:08 +0000 Subject: [PATCH 430/742] IRGen-level lowering for the Swift calling convention. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265324 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/CodeGen/SwiftCallingConv.h | 168 +++++ lib/CodeGen/ABIInfo.h | 51 +- lib/CodeGen/CGCall.cpp | 178 ++++- lib/CodeGen/CMakeLists.txt | 1 + lib/CodeGen/CodeGenFunction.h | 4 +- lib/CodeGen/CodeGenModule.h | 2 +- lib/CodeGen/CodeGenTypes.h | 2 +- lib/CodeGen/SwiftCallingConv.cpp | 830 +++++++++++++++++++++++ lib/CodeGen/TargetInfo.cpp | 87 ++- lib/CodeGen/TargetInfo.h | 5 +- test/CodeGen/arm-swiftcall.c | 496 ++++++++++++++ test/CodeGenCXX/arm-swiftcall.cpp | 115 ++++ 12 files changed, 1894 insertions(+), 45 deletions(-) create mode 100644 include/clang/CodeGen/SwiftCallingConv.h create mode 100644 lib/CodeGen/SwiftCallingConv.cpp create mode 100644 test/CodeGen/arm-swiftcall.c create mode 100644 test/CodeGenCXX/arm-swiftcall.cpp diff --git a/include/clang/CodeGen/SwiftCallingConv.h b/include/clang/CodeGen/SwiftCallingConv.h new file mode 100644 index 00000000000..f9c2fd94ca8 --- /dev/null +++ b/include/clang/CodeGen/SwiftCallingConv.h @@ -0,0 +1,168 @@ +//==-- SwiftCallingConv.h - Swift ABI lowering -----------------------------==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Defines constants and types related to Swift ABI lowering. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CODEGEN_SWIFTCALLINGCONV_H +#define LLVM_CLANG_CODEGEN_SWIFTCALLINGCONV_H + +#include "clang/AST/CanonicalType.h" +#include "clang/AST/CharUnits.h" +#include "clang/AST/Type.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/Support/TrailingObjects.h" +#include + +namespace llvm { + class IntegerType; + class Type; + class StructType; + class VectorType; +} + +namespace clang { +class Decl; +class FieldDecl; +class ASTRecordLayout; + +namespace CodeGen { +class ABIArgInfo; +class CodeGenModule; +class CGFunctionInfo; + +namespace swiftcall { + +class SwiftAggLowering { + CodeGenModule &CGM; + + struct StorageEntry { + CharUnits Begin; + CharUnits End; + llvm::Type *Type; + + CharUnits getWidth() const { + return End - Begin; + } + }; + SmallVector Entries; + bool Finished = false; + +public: + SwiftAggLowering(CodeGenModule &CGM) : CGM(CGM) {} + + void addOpaqueData(CharUnits begin, CharUnits end) { + addEntry(nullptr, begin, end); + } + + void addTypedData(QualType type, CharUnits begin); + void addTypedData(const RecordDecl *record, CharUnits begin); + void addTypedData(const RecordDecl *record, CharUnits begin, + const ASTRecordLayout &layout); + void addTypedData(llvm::Type *type, CharUnits begin); + void addTypedData(llvm::Type *type, CharUnits begin, CharUnits end); + + void finish(); + + /// Does this lowering require passing any data? + bool empty() const { + assert(Finished && "didn't finish lowering before calling empty()"); + return Entries.empty(); + } + + /// According to the target Swift ABI, should a value with this lowering + /// be passed indirectly? + /// + /// Note that this decision is based purely on the data layout of the + /// value and does not consider whether the type is address-only, + /// must be passed indirectly to match a function abstraction pattern, or + /// anything else that is expected to be handled by high-level lowering. + /// + /// \param asReturnValue - if true, answer whether it should be passed + /// indirectly as a return value; if false, answer whether it should be + /// passed indirectly as an argument + bool shouldPassIndirectly(bool asReturnValue) const; + + using EnumerationCallback = + llvm::function_ref; + + /// Enumerate the expanded components of this type. + /// + /// The component types will always be legal vector, floating-point, + /// integer, or pointer types. + void enumerateComponents(EnumerationCallback callback) const; + + /// Return the types for a coerce-and-expand operation. + /// + /// The first type matches the memory layout of the data that's been + /// added to this structure, including explicit [N x i8] arrays for any + /// internal padding. + /// + /// The second type removes any internal padding members and, if only + /// one element remains, is simply that element type. + std::pair getCoerceAndExpandTypes() const; + +private: + void addBitFieldData(const FieldDecl *field, CharUnits begin, + uint64_t bitOffset); + void addLegalTypedData(llvm::Type *type, CharUnits begin, CharUnits end); + void addEntry(llvm::Type *type, CharUnits begin, CharUnits end); + void splitVectorEntry(unsigned index); +}; + +/// Return the maximum voluntary integer size for the current target. +CharUnits getMaximumVoluntaryIntegerSize(CodeGenModule &CGM); + +/// Return the Swift CC's notion of the natural alignment of a type. +CharUnits getNaturalAlignment(CodeGenModule &CGM, llvm::Type *type); + +/// Is the given integer type "legal" for Swift's perspective on the +/// current platform? +bool isLegalIntegerType(CodeGenModule &CGM, llvm::IntegerType *type); + +/// Is the given vector type "legal" for Swift's perspective on the +/// current platform? +bool isLegalVectorType(CodeGenModule &CGM, CharUnits vectorSize, + llvm::VectorType *vectorTy); +bool isLegalVectorType(CodeGenModule &CGM, CharUnits vectorSize, + llvm::Type *eltTy, unsigned numElts); + +/// Minimally split a legal vector type. +std::pair +splitLegalVectorType(CodeGenModule &CGM, CharUnits vectorSize, + llvm::VectorType *vectorTy); + +/// Turn a vector type in a sequence of legal component vector types. +/// +/// The caller may assume that the sum of the data sizes of the resulting +/// types will equal the data size of the vector type. +void legalizeVectorType(CodeGenModule &CGM, CharUnits vectorSize, + llvm::VectorType *vectorTy, + llvm::SmallVectorImpl &types); + +/// Should a C++ record type be passed and returned indirectly? +bool shouldPassCXXRecordIndirectly(CodeGenModule &CGM, + const CXXRecordDecl *record); + +/// Classify the rules for how to return a particular type. +ABIArgInfo classifyReturnType(CodeGenModule &CGM, CanQualType type); + +/// Classify the rules for how to pass a particular type. +ABIArgInfo classifyArgumentType(CodeGenModule &CGM, CanQualType type); + +/// Compute the ABI information of a swiftcall function. This is a +/// private interface for Clang. +void computeABIInfo(CodeGenModule &CGM, CGFunctionInfo &FI); + +} // end namespace swiftcall +} // end namespace CodeGen +} // end namespace clang + +#endif diff --git a/lib/CodeGen/ABIInfo.h b/lib/CodeGen/ABIInfo.h index a65f2708561..bf462906506 100644 --- a/lib/CodeGen/ABIInfo.h +++ b/lib/CodeGen/ABIInfo.h @@ -18,20 +18,25 @@ namespace llvm { class Value; class LLVMContext; class DataLayout; + class Type; } namespace clang { class ASTContext; class TargetInfo; - namespace CodeGen { - class ABIArgInfo; - class Address; - class CGCXXABI; - class CGFunctionInfo; - class CodeGenFunction; - class CodeGenTypes; - } +namespace CodeGen { + class ABIArgInfo; + class Address; + class CGCXXABI; + class CGFunctionInfo; + class CodeGenFunction; + class CodeGenTypes; + class SwiftABIInfo; + +namespace swiftcall { + class SwiftAggLowering; +} // FIXME: All of this stuff should be part of the target interface // somehow. It is currently here because it is not clear how to factor @@ -55,6 +60,8 @@ namespace clang { virtual ~ABIInfo(); + virtual bool supportsSwift() const { return false; } + CodeGen::CGCXXABI &getCXXABI() const; ASTContext &getContext() const; llvm::LLVMContext &getVMContext() const; @@ -110,7 +117,35 @@ namespace clang { CodeGen::ABIArgInfo getNaturalAlignIndirectInReg(QualType Ty, bool Realign = false) const; + + }; + + /// A refining implementation of ABIInfo for targets that support swiftcall. + /// + /// If we find ourselves wanting multiple such refinements, they'll probably + /// be independent refinements, and we should probably find another way + /// to do it than simple inheritance. + class SwiftABIInfo : public ABIInfo { + public: + SwiftABIInfo(CodeGen::CodeGenTypes &cgt) : ABIInfo(cgt) {} + + bool supportsSwift() const final override { return true; } + + virtual bool shouldPassIndirectlyForSwift(CharUnits totalSize, + ArrayRef types, + bool asReturnValue) const = 0; + + virtual bool isLegalVectorTypeForSwift(CharUnits totalSize, + llvm::Type *eltTy, + unsigned elts) const; + + static bool classof(const ABIInfo *info) { + return info->supportsSwift(); + } + }; + +} // end namespace CodeGen } // end namespace clang #endif diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index 3711d83a92c..785fb225529 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -25,6 +25,7 @@ #include "clang/Basic/TargetBuiltins.h" #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/CGFunctionInfo.h" +#include "clang/CodeGen/SwiftCallingConv.h" #include "clang/Frontend/CodeGenOptions.h" #include "llvm/ADT/StringExtras.h" #include "llvm/IR/Attributes.h" @@ -58,6 +59,7 @@ static unsigned ClangCallConvToLLVMCallConv(CallingConv CC) { case CC_SpirKernel: return llvm::CallingConv::SPIR_KERNEL; case CC_PreserveMost: return llvm::CallingConv::PreserveMost; case CC_PreserveAll: return llvm::CallingConv::PreserveAll; + case CC_Swift: return llvm::CallingConv::Swift; } } @@ -108,7 +110,7 @@ static void appendParameterTypes(const CodeGenTypes &CGT, auto protoParamInfos = FPT->getExtParameterInfos(); paramInfos.reserve(prefix.size() + protoParamInfos.size()); paramInfos.resize(prefix.size()); - paramInfos.append(paramInfos.begin(), paramInfos.end()); + paramInfos.append(protoParamInfos.begin(), protoParamInfos.end()); } // Fast path: unknown target. @@ -589,7 +591,6 @@ CodeGenTypes::arrangeBuiltinFunctionDeclaration(CanQualType resultType, argTypes, FunctionType::ExtInfo(), {}, RequiredArgs::All); } - /// Arrange a call to a C++ method, passing the given arguments. const CGFunctionInfo & CodeGenTypes::arrangeCXXMethodCall(const CallArgList &args, @@ -678,7 +679,11 @@ CodeGenTypes::arrangeLLVMFunctionInfo(CanQualType resultType, assert(inserted && "Recursively being processed?"); // Compute ABI information. - getABIInfo().computeInfo(*FI); + if (info.getCC() != CC_Swift) { + getABIInfo().computeInfo(*FI); + } else { + swiftcall::computeABIInfo(CGM, *FI); + } // Loop over all of the computed argument and return value info. If any of // them are direct or extend without a specified coerce type, specify the @@ -916,7 +921,7 @@ static void forConstantArrayExpansion(CodeGenFunction &CGF, } void CodeGenFunction::ExpandTypeFromArgs( - QualType Ty, LValue LV, SmallVectorImpl::iterator &AI) { + QualType Ty, LValue LV, SmallVectorImpl::iterator &AI) { assert(LV.isSimple() && "Unexpected non-simple lvalue during struct expansion."); @@ -1800,10 +1805,13 @@ void CodeGenModule::ConstructAttributeList( getLLVMContext(), llvm::AttributeSet::ReturnIndex, RetAttrs)); } + bool hasUsedSRet = false; + // Attach attributes to sret. if (IRFunctionArgs.hasSRetArg()) { llvm::AttrBuilder SRETAttrs; SRETAttrs.addAttribute(llvm::Attribute::StructRet); + hasUsedSRet = true; if (RetAI.getInReg()) SRETAttrs.addAttribute(llvm::Attribute::InReg); PAL.push_back(llvm::AttributeSet::get( @@ -1907,6 +1915,41 @@ void CodeGenModule::ConstructAttributeList( Attrs.addAttribute(llvm::Attribute::NonNull); } + switch (FI.getExtParameterInfo(ArgNo).getABI()) { + case ParameterABI::Ordinary: + break; + + case ParameterABI::SwiftIndirectResult: { + // Add 'sret' if we haven't already used it for something, but + // only if the result is void. + if (!hasUsedSRet && RetTy->isVoidType()) { + Attrs.addAttribute(llvm::Attribute::StructRet); + hasUsedSRet = true; + } + + // Add 'noalias' in either case. + Attrs.addAttribute(llvm::Attribute::NoAlias); + + // Add 'dereferenceable' and 'alignment'. + auto PTy = ParamType->getPointeeType(); + if (!PTy->isIncompleteType() && PTy->isConstantSizeType()) { + auto info = getContext().getTypeInfoInChars(PTy); + Attrs.addDereferenceableAttr(info.first.getQuantity()); + Attrs.addAttribute(llvm::Attribute::getWithAlignment(getLLVMContext(), + info.second.getQuantity())); + } + break; + } + + case ParameterABI::SwiftErrorResult: + Attrs.addAttribute(llvm::Attribute::SwiftError); + break; + + case ParameterABI::SwiftContext: + Attrs.addAttribute(llvm::Attribute::SwiftSelf); + break; + } + if (Attrs.hasAttributes()) { unsigned FirstIRArg, NumIRArgs; std::tie(FirstIRArg, NumIRArgs) = IRFunctionArgs.getIRArgs(ArgNo); @@ -1972,6 +2015,18 @@ static const NonNullAttr *getNonNullAttr(const Decl *FD, const ParmVarDecl *PVD, return nullptr; } +namespace { + struct CopyBackSwiftError final : EHScopeStack::Cleanup { + Address Temp; + Address Arg; + CopyBackSwiftError(Address temp, Address arg) : Temp(temp), Arg(arg) {} + void Emit(CodeGenFunction &CGF, Flags flags) override { + llvm::Value *errorValue = CGF.Builder.CreateLoad(Temp); + CGF.Builder.CreateStore(errorValue, Arg); + } + }; +} + void CodeGenFunction::EmitFunctionProlog(const CGFunctionInfo &FI, llvm::Function *Fn, const FunctionArgList &Args) { @@ -1997,7 +2052,7 @@ void CodeGenFunction::EmitFunctionProlog(const CGFunctionInfo &FI, ClangToLLVMArgMapping IRFunctionArgs(CGM.getContext(), FI); // Flattened function arguments. - SmallVector FnArgs; + SmallVector FnArgs; FnArgs.reserve(IRFunctionArgs.totalIRArgs()); for (auto &Arg : Fn->args()) { FnArgs.push_back(&Arg); @@ -2018,7 +2073,7 @@ void CodeGenFunction::EmitFunctionProlog(const CGFunctionInfo &FI, // Name the struct return parameter. if (IRFunctionArgs.hasSRetArg()) { - auto AI = FnArgs[IRFunctionArgs.getSRetArgNo()]; + auto AI = cast(FnArgs[IRFunctionArgs.getSRetArgNo()]); AI->setName("agg.result"); AI->addAttr(llvm::AttributeSet::get(getLLVMContext(), AI->getArgNo() + 1, llvm::Attribute::NoAlias)); @@ -2106,8 +2161,8 @@ void CodeGenFunction::EmitFunctionProlog(const CGFunctionInfo &FI, ArgI.getCoerceToType() == ConvertType(Ty) && ArgI.getDirectOffset() == 0) { assert(NumIRArgs == 1); - auto AI = FnArgs[FirstIRArg]; - llvm::Value *V = AI; + llvm::Value *V = FnArgs[FirstIRArg]; + auto AI = cast(V); if (const ParmVarDecl *PVD = dyn_cast(Arg)) { if (getNonNullAttr(CurCodeDecl, PVD, PVD->getType(), @@ -2176,6 +2231,25 @@ void CodeGenFunction::EmitFunctionProlog(const CGFunctionInfo &FI, AI->getArgNo() + 1, llvm::Attribute::NoAlias)); + // LLVM expects swifterror parameters to be used in very restricted + // ways. Copy the value into a less-restricted temporary. + if (FI.getExtParameterInfo(ArgNo).getABI() + == ParameterABI::SwiftErrorResult) { + QualType pointeeTy = Ty->getPointeeType(); + assert(pointeeTy->isPointerType()); + Address temp = + CreateMemTemp(pointeeTy, getPointerAlign(), "swifterror.temp"); + Address arg = Address(V, getContext().getTypeAlignInChars(pointeeTy)); + llvm::Value *incomingErrorValue = Builder.CreateLoad(arg); + Builder.CreateStore(incomingErrorValue, temp); + V = temp.getPointer(); + + // Push a cleanup to copy the value back at the end of the function. + // The convention does not guarantee that the value will be written + // back if the function exits with an unwind exception. + EHStack.pushCleanup(NormalCleanup, temp, arg); + } + // Ensure the argument is the correct type. if (V->getType() != ArgI.getCoerceToType()) V = Builder.CreateBitCast(V, ArgI.getCoerceToType()); @@ -3445,6 +3519,9 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, } } + Address swiftErrorTemp = Address::invalid(); + Address swiftErrorArg = Address::invalid(); + assert(CallInfo.arg_size() == CallArgs.size() && "Mismatch between function signature & arguments."); unsigned ArgNo = 0; @@ -3551,6 +3628,25 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, else V = Builder.CreateLoad(RV.getAggregateAddress()); + // Implement swifterror by copying into a new swifterror argument. + // We'll write back in the normal path out of the call. + if (CallInfo.getExtParameterInfo(ArgNo).getABI() + == ParameterABI::SwiftErrorResult) { + assert(!swiftErrorTemp.isValid() && "multiple swifterror args"); + + QualType pointeeTy = I->Ty->getPointeeType(); + swiftErrorArg = + Address(V, getContext().getTypeAlignInChars(pointeeTy)); + + swiftErrorTemp = + CreateMemTemp(pointeeTy, getPointerAlign(), "swifterror.temp"); + V = swiftErrorTemp.getPointer(); + cast(V)->setSwiftError(true); + + llvm::Value *errorValue = Builder.CreateLoad(swiftErrorArg); + Builder.CreateStore(errorValue, swiftErrorTemp); + } + // We might have to widen integers, but we should never truncate. if (ArgInfo.getCoerceToType() != V->getType() && V->getType()->isIntegerTy()) @@ -3561,6 +3657,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, if (FirstIRArg < IRFuncTy->getNumParams() && V->getType() != IRFuncTy->getParamType(FirstIRArg)) V = Builder.CreateBitCast(V, IRFuncTy->getParamType(FirstIRArg)); + IRCallArgs[FirstIRArg] = V; break; } @@ -3620,13 +3717,31 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, } case ABIArgInfo::CoerceAndExpand: { - assert(RV.isAggregate() && - "CoerceAndExpand does not support non-aggregate types yet"); - auto coercionType = ArgInfo.getCoerceAndExpandType(); auto layout = CGM.getDataLayout().getStructLayout(coercionType); - Address addr = RV.getAggregateAddress(); + llvm::Value *tempSize = nullptr; + Address addr = Address::invalid(); + if (RV.isAggregate()) { + addr = RV.getAggregateAddress(); + } else { + assert(RV.isScalar()); // complex should always just be direct + + llvm::Type *scalarType = RV.getScalarVal()->getType(); + auto scalarSize = CGM.getDataLayout().getTypeAllocSize(scalarType); + auto scalarAlign = CGM.getDataLayout().getPrefTypeAlignment(scalarType); + + tempSize = llvm::ConstantInt::get(CGM.Int64Ty, scalarSize); + + // Materialize to a temporary. + addr = CreateTempAlloca(RV.getScalarVal()->getType(), + CharUnits::fromQuantity(std::max(layout->getAlignment(), + scalarAlign))); + EmitLifetimeStart(scalarSize, addr.getPointer()); + + Builder.CreateStore(RV.getScalarVal(), addr); + } + addr = Builder.CreateElementBitCast(addr, coercionType); unsigned IRArgPos = FirstIRArg; @@ -3639,6 +3754,10 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, } assert(IRArgPos == FirstIRArg + NumIRArgs); + if (tempSize) { + EmitLifetimeEnd(tempSize, addr.getPointer()); + } + break; } @@ -3810,6 +3929,12 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, if (Builder.isNamePreserving() && !CI->getType()->isVoidTy()) CI->setName("call"); + // Perform the swifterror writeback. + if (swiftErrorTemp.isValid()) { + llvm::Value *errorResult = Builder.CreateLoad(swiftErrorTemp); + Builder.CreateStore(errorResult, swiftErrorArg); + } + // Emit any writebacks immediately. Arguably this should happen // after any return-value munging. if (CallArgs.hasWritebacks()) @@ -3827,15 +3952,6 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, RValue Ret = [&] { switch (RetAI.getKind()) { - case ABIArgInfo::InAlloca: - case ABIArgInfo::Indirect: { - RValue ret = convertTempToRValue(SRetPtr, RetTy, SourceLocation()); - if (UnusedReturnSize) - EmitLifetimeEnd(llvm::ConstantInt::get(Int64Ty, UnusedReturnSize), - SRetPtr.getPointer()); - return ret; - } - case ABIArgInfo::CoerceAndExpand: { auto coercionType = RetAI.getCoerceAndExpandType(); auto layout = CGM.getDataLayout().getStructLayout(coercionType); @@ -3843,15 +3959,31 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, Address addr = SRetPtr; addr = Builder.CreateElementBitCast(addr, coercionType); + assert(CI->getType() == RetAI.getUnpaddedCoerceAndExpandType()); + bool requiresExtract = isa(CI->getType()); + unsigned unpaddedIndex = 0; for (unsigned i = 0, e = coercionType->getNumElements(); i != e; ++i) { llvm::Type *eltType = coercionType->getElementType(i); if (ABIArgInfo::isPaddingForCoerceAndExpand(eltType)) continue; Address eltAddr = Builder.CreateStructGEP(addr, i, layout); - llvm::Value *elt = Builder.CreateExtractValue(CI, unpaddedIndex++); + llvm::Value *elt = CI; + if (requiresExtract) + elt = Builder.CreateExtractValue(elt, unpaddedIndex++); + else + assert(unpaddedIndex == 0); Builder.CreateStore(elt, eltAddr); } - break; + // FALLTHROUGH + } + + case ABIArgInfo::InAlloca: + case ABIArgInfo::Indirect: { + RValue ret = convertTempToRValue(SRetPtr, RetTy, SourceLocation()); + if (UnusedReturnSize) + EmitLifetimeEnd(llvm::ConstantInt::get(Int64Ty, UnusedReturnSize), + SRetPtr.getPointer()); + return ret; } case ABIArgInfo::Ignore: diff --git a/lib/CodeGen/CMakeLists.txt b/lib/CodeGen/CMakeLists.txt index 10bda76b6b6..6ec1ebbf53e 100644 --- a/lib/CodeGen/CMakeLists.txt +++ b/lib/CodeGen/CMakeLists.txt @@ -74,6 +74,7 @@ add_clang_library(clangCodeGen ModuleBuilder.cpp ObjectFilePCHContainerOperations.cpp SanitizerMetadata.cpp + SwiftCallingConv.cpp TargetInfo.cpp DEPENDS diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index 961d6aed0f6..ed2718e16cd 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -68,7 +68,6 @@ class ObjCMethodDecl; class ObjCImplementationDecl; class ObjCPropertyImplDecl; class TargetInfo; -class TargetCodeGenInfo; class VarDecl; class ObjCForCollectionStmt; class ObjCAtTryStmt; @@ -86,6 +85,7 @@ class BlockByrefHelpers; class BlockByrefInfo; class BlockFlags; class BlockFieldFlags; +class TargetCodeGenInfo; /// The kind of evaluation to perform on values of a particular /// type. Basically, is the code in CGExprScalar, CGExprComplex, or @@ -3067,7 +3067,7 @@ class CodeGenFunction : public CodeGenTypeCache { /// /// \param AI - The first function argument of the expansion. void ExpandTypeFromArgs(QualType Ty, LValue Dst, - SmallVectorImpl::iterator &AI); + SmallVectorImpl::iterator &AI); /// ExpandTypeToArgs - Expand an RValue \arg RV, with the LLVM type for \arg /// Ty, into individual arguments on the provided vector \arg IRCallArgs, diff --git a/lib/CodeGen/CodeGenModule.h b/lib/CodeGen/CodeGenModule.h index efebcdac702..b012127432a 100644 --- a/lib/CodeGen/CodeGenModule.h +++ b/lib/CodeGen/CodeGenModule.h @@ -48,7 +48,6 @@ class IndexedInstrProfReader; } namespace clang { -class TargetCodeGenInfo; class ASTContext; class AtomicType; class FunctionDecl; @@ -92,6 +91,7 @@ class CGCUDARuntime; class BlockFieldFlags; class FunctionArgList; class CoverageMappingModuleGen; +class TargetCodeGenInfo; struct OrderGlobalInits { unsigned int priority; diff --git a/lib/CodeGen/CodeGenTypes.h b/lib/CodeGen/CodeGenTypes.h index 34f6b611365..5a2f5c391f4 100644 --- a/lib/CodeGen/CodeGenTypes.h +++ b/lib/CodeGen/CodeGenTypes.h @@ -31,7 +31,6 @@ class StructType; } namespace clang { -class ABIInfo; class ASTContext; template class CanQual; class CXXConstructorDecl; @@ -51,6 +50,7 @@ class Type; typedef CanQual CanQualType; namespace CodeGen { +class ABIInfo; class CGCXXABI; class CGRecordLayout; class CodeGenModule; diff --git a/lib/CodeGen/SwiftCallingConv.cpp b/lib/CodeGen/SwiftCallingConv.cpp new file mode 100644 index 00000000000..6fae19f2779 --- /dev/null +++ b/lib/CodeGen/SwiftCallingConv.cpp @@ -0,0 +1,830 @@ +//===--- SwiftCallingConv.cpp - Lowering for the Swift calling convention -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implementation of the abstract lowering for the Swift calling convention. +// +//===----------------------------------------------------------------------===// + +#include "clang/CodeGen/SwiftCallingConv.h" +#include "clang/Basic/TargetInfo.h" +#include "CodeGenModule.h" +#include "TargetInfo.h" + +using namespace clang; +using namespace CodeGen; +using namespace swiftcall; + +static const SwiftABIInfo &getSwiftABIInfo(CodeGenModule &CGM) { + return cast(CGM.getTargetCodeGenInfo().getABIInfo()); +} + +static bool isPowerOf2(unsigned n) { + return n == (n & -n); +} + +/// Given two types with the same size, try to find a common type. +static llvm::Type *getCommonType(llvm::Type *first, llvm::Type *second) { + assert(first != second); + + // Allow pointers to merge with integers, but prefer the integer type. + if (first->isIntegerTy()) { + if (second->isPointerTy()) return first; + } else if (first->isPointerTy()) { + if (second->isIntegerTy()) return second; + if (second->isPointerTy()) return first; + + // Allow two vectors to be merged (given that they have the same size). + // This assumes that we never have two different vector register sets. + } else if (auto firstVecTy = dyn_cast(first)) { + if (auto secondVecTy = dyn_cast(second)) { + if (auto commonTy = getCommonType(firstVecTy->getElementType(), + secondVecTy->getElementType())) { + return (commonTy == firstVecTy->getElementType() ? first : second); + } + } + } + + return nullptr; +} + +static CharUnits getTypeStoreSize(CodeGenModule &CGM, llvm::Type *type) { + return CharUnits::fromQuantity(CGM.getDataLayout().getTypeStoreSize(type)); +} + +void SwiftAggLowering::addTypedData(QualType type, CharUnits begin) { + // Deal with various aggregate types as special cases: + + // Record types. + if (auto recType = type->getAs()) { + addTypedData(recType->getDecl(), begin); + + // Array types. + } else if (type->isArrayType()) { + // Incomplete array types (flexible array members?) don't provide + // data to lay out, and the other cases shouldn't be possible. + auto arrayType = CGM.getContext().getAsConstantArrayType(type); + if (!arrayType) return; + + QualType eltType = arrayType->getElementType(); + auto eltSize = CGM.getContext().getTypeSizeInChars(eltType); + for (uint64_t i = 0, e = arrayType->getSize().getZExtValue(); i != e; ++i) { + addTypedData(eltType, begin + i * eltSize); + } + + // Complex types. + } else if (auto complexType = type->getAs()) { + auto eltType = complexType->getElementType(); + auto eltSize = CGM.getContext().getTypeSizeInChars(eltType); + auto eltLLVMType = CGM.getTypes().ConvertType(eltType); + addTypedData(eltLLVMType, begin, begin + eltSize); + addTypedData(eltLLVMType, begin + eltSize, begin + 2 * eltSize); + + // Member pointer types. + } else if (type->getAs()) { + // Just add it all as opaque. + addOpaqueData(begin, begin + CGM.getContext().getTypeSizeInChars(type)); + + // Everything else is scalar and should not convert as an LLVM aggregate. + } else { + // We intentionally convert as !ForMem because we want to preserve + // that a type was an i1. + auto llvmType = CGM.getTypes().ConvertType(type); + addTypedData(llvmType, begin); + } +} + +void SwiftAggLowering::addTypedData(const RecordDecl *record, CharUnits begin) { + addTypedData(record, begin, CGM.getContext().getASTRecordLayout(record)); +} + +void SwiftAggLowering::addTypedData(const RecordDecl *record, CharUnits begin, + const ASTRecordLayout &layout) { + // Unions are a special case. + if (record->isUnion()) { + for (auto field : record->fields()) { + if (field->isBitField()) { + addBitFieldData(field, begin, 0); + } else { + addTypedData(field->getType(), begin); + } + } + return; + } + + // Note that correctness does not rely on us adding things in + // their actual order of layout; it's just somewhat more efficient + // for the builder. + + // With that in mind, add "early" C++ data. + auto cxxRecord = dyn_cast(record); + if (cxxRecord) { + // - a v-table pointer, if the class adds its own + if (layout.hasOwnVFPtr()) { + addTypedData(CGM.Int8PtrTy, begin); + } + + // - non-virtual bases + for (auto &baseSpecifier : cxxRecord->bases()) { + if (baseSpecifier.isVirtual()) continue; + + auto baseRecord = baseSpecifier.getType()->getAsCXXRecordDecl(); + addTypedData(baseRecord, begin + layout.getBaseClassOffset(baseRecord)); + } + + // - a vbptr if the class adds its own + if (layout.hasOwnVBPtr()) { + addTypedData(CGM.Int8PtrTy, begin + layout.getVBPtrOffset()); + } + } + + // Add fields. + for (auto field : record->fields()) { + auto fieldOffsetInBits = layout.getFieldOffset(field->getFieldIndex()); + if (field->isBitField()) { + addBitFieldData(field, begin, fieldOffsetInBits); + } else { + addTypedData(field->getType(), + begin + CGM.getContext().toCharUnitsFromBits(fieldOffsetInBits)); + } + } + + // Add "late" C++ data: + if (cxxRecord) { + // - virtual bases + for (auto &vbaseSpecifier : cxxRecord->vbases()) { + auto baseRecord = vbaseSpecifier.getType()->getAsCXXRecordDecl(); + addTypedData(baseRecord, begin + layout.getVBaseClassOffset(baseRecord)); + } + } +} + +void SwiftAggLowering::addBitFieldData(const FieldDecl *bitfield, + CharUnits recordBegin, + uint64_t bitfieldBitBegin) { + assert(bitfield->isBitField()); + auto &ctx = CGM.getContext(); + auto width = bitfield->getBitWidthValue(ctx); + + // We can ignore zero-width bit-fields. + if (width == 0) return; + + // toCharUnitsFromBits rounds down. + CharUnits bitfieldByteBegin = ctx.toCharUnitsFromBits(bitfieldBitBegin); + + // Find the offset of the last byte that is partially occupied by the + // bit-field; since we otherwise expect exclusive ends, the end is the + // next byte. + uint64_t bitfieldBitLast = bitfieldBitBegin + width - 1; + CharUnits bitfieldByteEnd = + ctx.toCharUnitsFromBits(bitfieldBitLast) + CharUnits::One(); + addOpaqueData(recordBegin + bitfieldByteBegin, + recordBegin + bitfieldByteEnd); +} + +void SwiftAggLowering::addTypedData(llvm::Type *type, CharUnits begin) { + assert(type && "didn't provide type for typed data"); + addTypedData(type, begin, begin + getTypeStoreSize(CGM, type)); +} + +void SwiftAggLowering::addTypedData(llvm::Type *type, + CharUnits begin, CharUnits end) { + assert(type && "didn't provide type for typed data"); + assert(getTypeStoreSize(CGM, type) == end - begin); + + // Legalize vector types. + if (auto vecTy = dyn_cast(type)) { + SmallVector componentTys; + legalizeVectorType(CGM, end - begin, vecTy, componentTys); + assert(componentTys.size() >= 1); + + // Walk the initial components. + for (size_t i = 0, e = componentTys.size(); i != e - 1; ++i) { + llvm::Type *componentTy = componentTys[i]; + auto componentSize = getTypeStoreSize(CGM, componentTy); + assert(componentSize < end - begin); + addLegalTypedData(componentTy, begin, begin + componentSize); + begin += componentSize; + } + + return addLegalTypedData(componentTys.back(), begin, end); + } + + // Legalize integer types. + if (auto intTy = dyn_cast(type)) { + if (!isLegalIntegerType(CGM, intTy)) + return addOpaqueData(begin, end); + } + + // All other types should be legal. + return addLegalTypedData(type, begin, end); +} + +void SwiftAggLowering::addLegalTypedData(llvm::Type *type, + CharUnits begin, CharUnits end) { + // Require the type to be naturally aligned. + if (!begin.isZero() && !begin.isMultipleOf(getNaturalAlignment(CGM, type))) { + + // Try splitting vector types. + if (auto vecTy = dyn_cast(type)) { + auto split = splitLegalVectorType(CGM, end - begin, vecTy); + auto eltTy = split.first; + auto numElts = split.second; + + auto eltSize = (end - begin) / numElts; + assert(eltSize == getTypeStoreSize(CGM, eltTy)); + for (size_t i = 0, e = numElts; i != e; ++i) { + addLegalTypedData(type, begin, begin + eltSize); + begin += eltSize; + } + assert(begin == end); + return; + } + + return addOpaqueData(begin, end); + } + + addEntry(type, begin, end); +} + +void SwiftAggLowering::addEntry(llvm::Type *type, + CharUnits begin, CharUnits end) { + assert(!type || + (!isa(type) && !isa(type)) && + "cannot add aggregate-typed data"); + assert(!type || begin.isMultipleOf(getNaturalAlignment(CGM, type))); + + // Fast path: we can just add entries to the end. + if (Entries.empty() || Entries.back().End <= begin) { + Entries.push_back({begin, end, type}); + return; + } + + // Find the first existing entry that ends after the start of the new data. + // TODO: do a binary search if Entries is big enough for it to matter. + size_t index = Entries.size() - 1; + while (index != 0) { + if (Entries[index - 1].End <= begin) break; + --index; + } + + // The entry ends after the start of the new data. + // If the entry starts after the end of the new data, there's no conflict. + if (Entries[index].Begin >= end) { + // This insertion is potentially O(n), but the way we generally build + // these layouts makes that unlikely to matter: we'd need a union of + // several very large types. + Entries.insert(Entries.begin() + index, {begin, end, type}); + return; + } + + // Otherwise, the ranges overlap. The new range might also overlap + // with later ranges. +restartAfterSplit: + + // Simplest case: an exact overlap. + if (Entries[index].Begin == begin && Entries[index].End == end) { + // If the types match exactly, great. + if (Entries[index].Type == type) return; + + // If either type is opaque, make the entry opaque and return. + if (Entries[index].Type == nullptr) { + return; + } else if (type == nullptr) { + Entries[index].Type = nullptr; + return; + } + + // If they disagree in an ABI-agnostic way, just resolve the conflict + // arbitrarily. + if (auto entryType = getCommonType(Entries[index].Type, type)) { + Entries[index].Type = entryType; + return; + } + + // Otherwise, make the entry opaque. + Entries[index].Type = nullptr; + return; + } + + // Okay, we have an overlapping conflict of some sort. + + // If we have a vector type, split it. + if (auto vecTy = dyn_cast_or_null(type)) { + auto eltTy = vecTy->getElementType(); + CharUnits eltSize = (end - begin) / vecTy->getNumElements(); + assert(eltSize == getTypeStoreSize(CGM, eltTy)); + for (unsigned i = 0, e = vecTy->getNumElements(); i != e; ++i) { + addEntry(eltTy, begin, begin + eltSize); + begin += eltSize; + } + assert(begin == end); + return; + } + + // If the entry is a vector type, split it and try again. + if (Entries[index].Type && Entries[index].Type->isVectorTy()) { + splitVectorEntry(index); + goto restartAfterSplit; + } + + // Okay, we have no choice but to make the existing entry opaque. + + Entries[index].Type = nullptr; + + // Stretch the start of the entry to the beginning of the range. + if (begin < Entries[index].Begin) { + Entries[index].Begin = begin; + assert(index == 0 || begin >= Entries[index - 1].End); + } + + // Stretch the end of the entry to the end of the range; but if we run + // into the start of the next entry, just leave the range there and repeat. + while (end > Entries[index].End) { + assert(Entries[index].Type == nullptr); + + // If the range doesn't overlap the next entry, we're done. + if (index == Entries.size() - 1 || end <= Entries[index + 1].Begin) { + Entries[index].End = end; + break; + } + + // Otherwise, stretch to the start of the next entry. + Entries[index].End = Entries[index + 1].Begin; + + // Continue with the next entry. + index++; + + // This entry needs to be made opaque if it is not already. + if (Entries[index].Type == nullptr) + continue; + + // Split vector entries unless we completely subsume them. + if (Entries[index].Type->isVectorTy() && + end < Entries[index].End) { + splitVectorEntry(index); + } + + // Make the entry opaque. + Entries[index].Type = nullptr; + } +} + +/// Replace the entry of vector type at offset 'index' with a sequence +/// of its component vectors. +void SwiftAggLowering::splitVectorEntry(unsigned index) { + auto vecTy = cast(Entries[index].Type); + auto split = splitLegalVectorType(CGM, Entries[index].getWidth(), vecTy); + + auto eltTy = split.first; + CharUnits eltSize = getTypeStoreSize(CGM, eltTy); + auto numElts = split.second; + Entries.insert(&Entries[index + 1], numElts - 1, StorageEntry()); + + CharUnits begin = Entries[index].Begin; + for (unsigned i = 0; i != numElts; ++i) { + Entries[index].Type = eltTy; + Entries[index].Begin = begin; + Entries[index].End = begin + eltSize; + begin += eltSize; + } +} + +/// Given a power-of-two unit size, return the offset of the aligned unit +/// of that size which contains the given offset. +/// +/// In other words, round down to the nearest multiple of the unit size. +static CharUnits getOffsetAtStartOfUnit(CharUnits offset, CharUnits unitSize) { + assert(isPowerOf2(unitSize.getQuantity())); + auto unitMask = ~(unitSize.getQuantity() - 1); + return CharUnits::fromQuantity(offset.getQuantity() & unitMask); +} + +static bool areBytesInSameUnit(CharUnits first, CharUnits second, + CharUnits chunkSize) { + return getOffsetAtStartOfUnit(first, chunkSize) + == getOffsetAtStartOfUnit(second, chunkSize); +} + +void SwiftAggLowering::finish() { + if (Entries.empty()) { + Finished = true; + return; + } + + // We logically split the layout down into a series of chunks of this size, + // which is generally the size of a pointer. + const CharUnits chunkSize = getMaximumVoluntaryIntegerSize(CGM); + + // First pass: if two entries share a chunk, make them both opaque + // and stretch one to meet the next. + bool hasOpaqueEntries = (Entries[0].Type == nullptr); + for (size_t i = 1, e = Entries.size(); i != e; ++i) { + if (areBytesInSameUnit(Entries[i - 1].End - CharUnits::One(), + Entries[i].Begin, chunkSize)) { + Entries[i - 1].Type = nullptr; + Entries[i].Type = nullptr; + Entries[i - 1].End = Entries[i].Begin; + hasOpaqueEntries = true; + + } else if (Entries[i].Type == nullptr) { + hasOpaqueEntries = true; + } + } + + // The rest of the algorithm leaves non-opaque entries alone, so if we + // have no opaque entries, we're done. + if (!hasOpaqueEntries) { + Finished = true; + return; + } + + // Okay, move the entries to a temporary and rebuild Entries. + auto orig = std::move(Entries); + assert(Entries.empty()); + + for (size_t i = 0, e = orig.size(); i != e; ++i) { + // Just copy over non-opaque entries. + if (orig[i].Type != nullptr) { + Entries.push_back(orig[i]); + continue; + } + + // Scan forward to determine the full extent of the next opaque range. + // We know from the first pass that only contiguous ranges will overlap + // the same aligned chunk. + auto begin = orig[i].Begin; + auto end = orig[i].End; + while (i + 1 != e && + orig[i + 1].Type == nullptr && + end == orig[i + 1].Begin) { + end = orig[i + 1].End; + i++; + } + + // Add an entry per intersected chunk. + do { + // Find the smallest aligned storage unit in the maximal aligned + // storage unit containing 'begin' that contains all the bytes in + // the intersection between the range and this chunk. + CharUnits localBegin = begin; + CharUnits chunkBegin = getOffsetAtStartOfUnit(localBegin, chunkSize); + CharUnits chunkEnd = chunkBegin + chunkSize; + CharUnits localEnd = std::min(end, chunkEnd); + + // Just do a simple loop over ever-increasing unit sizes. + CharUnits unitSize = CharUnits::One(); + CharUnits unitBegin, unitEnd; + for (; ; unitSize *= 2) { + assert(unitSize <= chunkSize); + unitBegin = getOffsetAtStartOfUnit(localBegin, unitSize); + unitEnd = unitBegin + unitSize; + if (unitEnd >= localEnd) break; + } + + // Add an entry for this unit. + auto entryTy = + llvm::IntegerType::get(CGM.getLLVMContext(), + CGM.getContext().toBits(unitSize)); + Entries.push_back({unitBegin, unitEnd, entryTy}); + + // The next chunk starts where this chunk left off. + begin = localEnd; + } while (begin != end); + } + + // Okay, finally finished. + Finished = true; +} + +void SwiftAggLowering::enumerateComponents(EnumerationCallback callback) const { + assert(Finished && "haven't yet finished lowering"); + + for (auto &entry : Entries) { + callback(entry.Begin, entry.Type); + } +} + +std::pair +SwiftAggLowering::getCoerceAndExpandTypes() const { + assert(Finished && "haven't yet finished lowering"); + + auto &ctx = CGM.getLLVMContext(); + + if (Entries.empty()) { + auto type = llvm::StructType::get(ctx); + return { type, type }; + } + + SmallVector elts; + CharUnits lastEnd = CharUnits::Zero(); + bool hasPadding = false; + bool packed = false; + for (auto &entry : Entries) { + if (entry.Begin != lastEnd) { + auto paddingSize = entry.Begin - lastEnd; + assert(!paddingSize.isNegative()); + + auto padding = llvm::ArrayType::get(llvm::Type::getInt8Ty(ctx), + paddingSize.getQuantity()); + elts.push_back(padding); + hasPadding = true; + } + + if (!packed && !entry.Begin.isMultipleOf( + CharUnits::fromQuantity( + CGM.getDataLayout().getABITypeAlignment(entry.Type)))) + packed = true; + + elts.push_back(entry.Type); + lastEnd = entry.End; + } + + // We don't need to adjust 'packed' to deal with possible tail padding + // because we never do that kind of access through the coercion type. + auto coercionType = llvm::StructType::get(ctx, elts, packed); + + llvm::Type *unpaddedType = coercionType; + if (hasPadding) { + elts.clear(); + for (auto &entry : Entries) { + elts.push_back(entry.Type); + } + if (elts.size() == 1) { + unpaddedType = elts[0]; + } else { + unpaddedType = llvm::StructType::get(ctx, elts, /*packed*/ false); + } + } else if (Entries.size() == 1) { + unpaddedType = Entries[0].Type; + } + + return { coercionType, unpaddedType }; +} + +bool SwiftAggLowering::shouldPassIndirectly(bool asReturnValue) const { + assert(Finished && "haven't yet finished lowering"); + + // Empty types don't need to be passed indirectly. + if (Entries.empty()) return false; + + CharUnits totalSize = Entries.back().End; + + // Avoid copying the array of types when there's just a single element. + if (Entries.size() == 1) { + return getSwiftABIInfo(CGM).shouldPassIndirectlyForSwift(totalSize, + Entries.back().Type, + asReturnValue); + } + + SmallVector componentTys; + componentTys.reserve(Entries.size()); + for (auto &entry : Entries) { + componentTys.push_back(entry.Type); + } + return getSwiftABIInfo(CGM).shouldPassIndirectlyForSwift(totalSize, + componentTys, + asReturnValue); +} + +CharUnits swiftcall::getMaximumVoluntaryIntegerSize(CodeGenModule &CGM) { + // Currently always the size of an ordinary pointer. + return CGM.getContext().toCharUnitsFromBits( + CGM.getContext().getTargetInfo().getPointerWidth(0)); +} + +CharUnits swiftcall::getNaturalAlignment(CodeGenModule &CGM, llvm::Type *type) { + // For Swift's purposes, this is always just the store size of the type + // rounded up to a power of 2. + auto size = (unsigned long long) getTypeStoreSize(CGM, type).getQuantity(); + if (!isPowerOf2(size)) { + size = 1U << (llvm::findLastSet(size, llvm::ZB_Undefined) + 1); + } + assert(size >= CGM.getDataLayout().getABITypeAlignment(type)); + return CharUnits::fromQuantity(size); +} + +bool swiftcall::isLegalIntegerType(CodeGenModule &CGM, + llvm::IntegerType *intTy) { + auto size = intTy->getBitWidth(); + switch (size) { + case 1: + case 8: + case 16: + case 32: + case 64: + // Just assume that the above are always legal. + return true; + + case 128: + return CGM.getContext().getTargetInfo().hasInt128Type(); + + default: + return false; + } +} + +bool swiftcall::isLegalVectorType(CodeGenModule &CGM, CharUnits vectorSize, + llvm::VectorType *vectorTy) { + return isLegalVectorType(CGM, vectorSize, vectorTy->getElementType(), + vectorTy->getNumElements()); +} + +bool swiftcall::isLegalVectorType(CodeGenModule &CGM, CharUnits vectorSize, + llvm::Type *eltTy, unsigned numElts) { + assert(numElts > 1 && "illegal vector length"); + return getSwiftABIInfo(CGM) + .isLegalVectorTypeForSwift(vectorSize, eltTy, numElts); +} + +std::pair +swiftcall::splitLegalVectorType(CodeGenModule &CGM, CharUnits vectorSize, + llvm::VectorType *vectorTy) { + auto numElts = vectorTy->getNumElements(); + auto eltTy = vectorTy->getElementType(); + + // Try to split the vector type in half. + if (numElts >= 4 && isPowerOf2(numElts)) { + if (isLegalVectorType(CGM, vectorSize / 2, eltTy, numElts / 2)) + return {llvm::VectorType::get(eltTy, numElts / 2), 2}; + } + + return {eltTy, numElts}; +} + +void swiftcall::legalizeVectorType(CodeGenModule &CGM, CharUnits origVectorSize, + llvm::VectorType *origVectorTy, + llvm::SmallVectorImpl &components) { + // If it's already a legal vector type, use it. + if (isLegalVectorType(CGM, origVectorSize, origVectorTy)) { + components.push_back(origVectorTy); + return; + } + + // Try to split the vector into legal subvectors. + auto numElts = origVectorTy->getNumElements(); + auto eltTy = origVectorTy->getElementType(); + assert(numElts != 1); + + // The largest size that we're still considering making subvectors of. + // Always a power of 2. + unsigned logCandidateNumElts = llvm::findLastSet(numElts, llvm::ZB_Undefined); + unsigned candidateNumElts = 1U << logCandidateNumElts; + assert(candidateNumElts <= numElts && candidateNumElts * 2 > numElts); + + // Minor optimization: don't check the legality of this exact size twice. + if (candidateNumElts == numElts) { + logCandidateNumElts--; + candidateNumElts >>= 1; + } + + CharUnits eltSize = (origVectorSize / numElts); + CharUnits candidateSize = eltSize * candidateNumElts; + + // The sensibility of this algorithm relies on the fact that we never + // have a legal non-power-of-2 vector size without having the power of 2 + // also be legal. + while (logCandidateNumElts > 0) { + assert(candidateNumElts == 1U << logCandidateNumElts); + assert(candidateNumElts <= numElts); + assert(candidateSize == eltSize * candidateNumElts); + + // Skip illegal vector sizes. + if (!isLegalVectorType(CGM, candidateSize, eltTy, candidateNumElts)) { + logCandidateNumElts--; + candidateNumElts /= 2; + candidateSize /= 2; + continue; + } + + // Add the right number of vectors of this size. + auto numVecs = numElts >> logCandidateNumElts; + components.append(numVecs, llvm::VectorType::get(eltTy, candidateNumElts)); + numElts -= (numVecs << logCandidateNumElts); + + if (numElts == 0) return; + + // It's possible that the number of elements remaining will be legal. + // This can happen with e.g. <7 x float> when <3 x float> is legal. + // This only needs to be separately checked if it's not a power of 2. + if (numElts > 2 && !isPowerOf2(numElts) && + isLegalVectorType(CGM, eltSize * numElts, eltTy, numElts)) { + components.push_back(llvm::VectorType::get(eltTy, numElts)); + return; + } + + // Bring vecSize down to something no larger than numElts. + do { + logCandidateNumElts--; + candidateNumElts /= 2; + candidateSize /= 2; + } while (candidateNumElts > numElts); + } + + // Otherwise, just append a bunch of individual elements. + components.append(numElts, eltTy); +} + +bool swiftcall::shouldPassCXXRecordIndirectly(CodeGenModule &CGM, + const CXXRecordDecl *record) { + // Following a recommendation from Richard Smith, pass a C++ type + // indirectly only if the destructor is non-trivial or *all* of the + // copy/move constructors are deleted or non-trivial. + + if (record->hasNonTrivialDestructor()) + return true; + + // It would be nice if this were summarized on the CXXRecordDecl. + for (auto ctor : record->ctors()) { + if (ctor->isCopyOrMoveConstructor() && !ctor->isDeleted() && + ctor->isTrivial()) { + return false; + } + } + + return true; +} + +static ABIArgInfo classifyExpandedType(SwiftAggLowering &lowering, + bool forReturn, + CharUnits alignmentForIndirect) { + if (lowering.empty()) { + return ABIArgInfo::getIgnore(); + } else if (lowering.shouldPassIndirectly(forReturn)) { + return ABIArgInfo::getIndirect(alignmentForIndirect, /*byval*/ false); + } else { + auto types = lowering.getCoerceAndExpandTypes(); + return ABIArgInfo::getCoerceAndExpand(types.first, types.second); + } +} + +static ABIArgInfo classifyType(CodeGenModule &CGM, CanQualType type, + bool forReturn) { + if (auto recordType = dyn_cast(type)) { + auto record = recordType->getDecl(); + auto &layout = CGM.getContext().getASTRecordLayout(record); + + if (auto cxxRecord = dyn_cast(record)) { + if (shouldPassCXXRecordIndirectly(CGM, cxxRecord)) + return ABIArgInfo::getIndirect(layout.getAlignment(), /*byval*/ false); + } + + SwiftAggLowering lowering(CGM); + lowering.addTypedData(recordType->getDecl(), CharUnits::Zero(), layout); + lowering.finish(); + + return classifyExpandedType(lowering, forReturn, layout.getAlignment()); + } + + // Just assume that all of our target ABIs can support returning at least + // two integer or floating-point values. + if (isa(type)) { + return (forReturn ? ABIArgInfo::getDirect() : ABIArgInfo::getExpand()); + } + + // Vector types may need to be legalized. + if (isa(type)) { + SwiftAggLowering lowering(CGM); + lowering.addTypedData(type, CharUnits::Zero()); + lowering.finish(); + + CharUnits alignment = CGM.getContext().getTypeAlignInChars(type); + return classifyExpandedType(lowering, forReturn, alignment); + } + + // Member pointer types need to be expanded, but it's a simple form of + // expansion that 'Direct' can handle. Note that CanBeFlattened should be + // true for this to work. + + // 'void' needs to be ignored. + if (type->isVoidType()) { + return ABIArgInfo::getIgnore(); + } + + // Everything else can be passed directly. + return ABIArgInfo::getDirect(); +} + +ABIArgInfo swiftcall::classifyReturnType(CodeGenModule &CGM, CanQualType type) { + return classifyType(CGM, type, /*forReturn*/ true); +} + +ABIArgInfo swiftcall::classifyArgumentType(CodeGenModule &CGM, + CanQualType type) { + return classifyType(CGM, type, /*forReturn*/ false); +} + +void swiftcall::computeABIInfo(CodeGenModule &CGM, CGFunctionInfo &FI) { + auto &retInfo = FI.getReturnInfo(); + retInfo = classifyReturnType(CGM, FI.getReturnType()); + + for (unsigned i = 0, e = FI.arg_size(); i != e; ++i) { + auto &argInfo = FI.arg_begin()[i]; + argInfo.info = classifyArgumentType(CGM, argInfo.type); + } +} \ No newline at end of file diff --git a/lib/CodeGen/TargetInfo.cpp b/lib/CodeGen/TargetInfo.cpp index dc3bf33f0ec..08f1c1c9b29 100644 --- a/lib/CodeGen/TargetInfo.cpp +++ b/lib/CodeGen/TargetInfo.cpp @@ -19,6 +19,7 @@ #include "CodeGenFunction.h" #include "clang/AST/RecordLayout.h" #include "clang/CodeGen/CGFunctionInfo.h" +#include "clang/CodeGen/SwiftCallingConv.h" #include "clang/Frontend/CodeGenOptions.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Triple.h" @@ -68,6 +69,46 @@ Address ABIInfo::EmitMSVAArg(CodeGenFunction &CGF, Address VAListAddr, ABIInfo::~ABIInfo() {} +/// Does the given lowering require more than the given number of +/// registers when expanded? +/// +/// This is intended to be the basis of a reasonable basic implementation +/// of should{Pass,Return}IndirectlyForSwift. +/// +/// For most targets, a limit of four total registers is reasonable; this +/// limits the amount of code required in order to move around the value +/// in case it wasn't produced immediately prior to the call by the caller +/// (or wasn't produced in exactly the right registers) or isn't used +/// immediately within the callee. But some targets may need to further +/// limit the register count due to an inability to support that many +/// return registers. +static bool occupiesMoreThan(CodeGenTypes &cgt, + ArrayRef scalarTypes, + unsigned maxAllRegisters) { + unsigned intCount = 0, fpCount = 0; + for (llvm::Type *type : scalarTypes) { + if (type->isPointerTy()) { + intCount++; + } else if (auto intTy = dyn_cast(type)) { + auto ptrWidth = cgt.getTarget().getPointerWidth(0); + intCount += (intTy->getBitWidth() + ptrWidth - 1) / ptrWidth; + } else { + assert(type->isVectorTy() || type->isFloatingPointTy()); + fpCount++; + } + } + + return (intCount + fpCount > maxAllRegisters); +} + +bool SwiftABIInfo::isLegalVectorTypeForSwift(CharUnits vectorSize, + llvm::Type *eltTy, + unsigned numElts) const { + // The default implementation of this assumes that the target guarantees + // 128-bit SIMD support but nothing more. + return (vectorSize.getQuantity() > 8 && vectorSize.getQuantity() <= 16); +} + static CGCXXABI::RecordArgABI getRecordArgABI(const RecordType *RT, CGCXXABI &CXXABI) { const CXXRecordDecl *RD = dyn_cast(RT->getDecl()); @@ -801,7 +842,7 @@ struct CCState { }; /// X86_32ABIInfo - The X86-32 ABI information. -class X86_32ABIInfo : public ABIInfo { +class X86_32ABIInfo : public SwiftABIInfo { enum Class { Integer, Float @@ -870,12 +911,22 @@ class X86_32ABIInfo : public ABIInfo { X86_32ABIInfo(CodeGen::CodeGenTypes &CGT, bool DarwinVectorABI, bool RetSmallStructInRegABI, bool Win32StructABI, unsigned NumRegisterParameters, bool SoftFloatABI) - : ABIInfo(CGT), IsDarwinVectorABI(DarwinVectorABI), + : SwiftABIInfo(CGT), IsDarwinVectorABI(DarwinVectorABI), IsRetSmallStructInRegABI(RetSmallStructInRegABI), IsWin32StructABI(Win32StructABI), IsSoftFloatABI(SoftFloatABI), IsMCUABI(CGT.getTarget().getTriple().isOSIAMCU()), DefaultNumRegisterParameters(NumRegisterParameters) {} + + bool shouldPassIndirectlyForSwift(CharUnits totalSize, + ArrayRef scalars, + bool asReturnValue) const override { + // LLVM's x86-32 lowering currently only assigns up to three + // integer registers and three fp registers. Oddly, it'll use up to + // four vector registers for vectors, but those can overlap with the + // scalar registers. + return occupiesMoreThan(CGT, scalars, /*total*/ 3); + } }; class X86_32TargetCodeGenInfo : public TargetCodeGenInfo { @@ -1689,7 +1740,7 @@ static unsigned getNativeVectorSizeForAVXABI(X86AVXABILevel AVXLevel) { } /// X86_64ABIInfo - The X86_64 ABI information. -class X86_64ABIInfo : public ABIInfo { +class X86_64ABIInfo : public SwiftABIInfo { enum Class { Integer = 0, SSE, @@ -1800,7 +1851,7 @@ class X86_64ABIInfo : public ABIInfo { public: X86_64ABIInfo(CodeGen::CodeGenTypes &CGT, X86AVXABILevel AVXLevel) : - ABIInfo(CGT), AVXLevel(AVXLevel), + SwiftABIInfo(CGT), AVXLevel(AVXLevel), Has64BitPointers(CGT.getDataLayout().getPointerSize(0) == 8) { } @@ -1827,6 +1878,12 @@ class X86_64ABIInfo : public ABIInfo { bool has64BitPointers() const { return Has64BitPointers; } + + bool shouldPassIndirectlyForSwift(CharUnits totalSize, + ArrayRef scalars, + bool asReturnValue) const override { + return occupiesMoreThan(CGT, scalars, /*total*/ 4); + } }; /// WinX86_64ABIInfo - The Windows X86_64 ABI information. @@ -4248,7 +4305,7 @@ PPC64TargetCodeGenInfo::initDwarfEHRegSizeTable(CodeGen::CodeGenFunction &CGF, namespace { -class AArch64ABIInfo : public ABIInfo { +class AArch64ABIInfo : public SwiftABIInfo { public: enum ABIKind { AAPCS = 0, @@ -4259,7 +4316,8 @@ class AArch64ABIInfo : public ABIInfo { ABIKind Kind; public: - AArch64ABIInfo(CodeGenTypes &CGT, ABIKind Kind) : ABIInfo(CGT), Kind(Kind) {} + AArch64ABIInfo(CodeGenTypes &CGT, ABIKind Kind) + : SwiftABIInfo(CGT), Kind(Kind) {} private: ABIKind getABIKind() const { return Kind; } @@ -4292,6 +4350,12 @@ class AArch64ABIInfo : public ABIInfo { return isDarwinPCS() ? EmitDarwinVAArg(VAListAddr, Ty, CGF) : EmitAAPCSVAArg(VAListAddr, Ty, CGF); } + + bool shouldPassIndirectlyForSwift(CharUnits totalSize, + ArrayRef scalars, + bool asReturnValue) const override { + return occupiesMoreThan(CGT, scalars, /*total*/ 4); + } }; class AArch64TargetCodeGenInfo : public TargetCodeGenInfo { @@ -4761,7 +4825,7 @@ Address AArch64ABIInfo::EmitDarwinVAArg(Address VAListAddr, QualType Ty, namespace { -class ARMABIInfo : public ABIInfo { +class ARMABIInfo : public SwiftABIInfo { public: enum ABIKind { APCS = 0, @@ -4774,7 +4838,8 @@ class ARMABIInfo : public ABIInfo { ABIKind Kind; public: - ARMABIInfo(CodeGenTypes &CGT, ABIKind _Kind) : ABIInfo(CGT), Kind(_Kind) { + ARMABIInfo(CodeGenTypes &CGT, ABIKind _Kind) + : SwiftABIInfo(CGT), Kind(_Kind) { setCCs(); } @@ -4825,6 +4890,12 @@ class ARMABIInfo : public ABIInfo { llvm::CallingConv::ID getLLVMDefaultCC() const; llvm::CallingConv::ID getABIDefaultCC() const; void setCCs(); + + bool shouldPassIndirectlyForSwift(CharUnits totalSize, + ArrayRef scalars, + bool asReturnValue) const override { + return occupiesMoreThan(CGT, scalars, /*total*/ 4); + } }; class ARMTargetCodeGenInfo : public TargetCodeGenInfo { diff --git a/lib/CodeGen/TargetInfo.h b/lib/CodeGen/TargetInfo.h index 87b47049862..71f6b0a4c5c 100644 --- a/lib/CodeGen/TargetInfo.h +++ b/lib/CodeGen/TargetInfo.h @@ -29,15 +29,14 @@ class Value; } namespace clang { -class ABIInfo; class Decl; namespace CodeGen { +class ABIInfo; class CallArgList; class CodeGenModule; class CodeGenFunction; class CGFunctionInfo; -} /// TargetCodeGenInfo - This class organizes various target-specific /// codegeneration issues, like target-specific attributes, builtins and so @@ -219,6 +218,8 @@ class TargetCodeGenInfo { llvm::StringRef Value, llvm::SmallString<32> &Opt) const {} }; + +} // namespace CodeGen } // namespace clang #endif // LLVM_CLANG_LIB_CODEGEN_TARGETINFO_H diff --git a/test/CodeGen/arm-swiftcall.c b/test/CodeGen/arm-swiftcall.c new file mode 100644 index 00000000000..dc1d68e34b9 --- /dev/null +++ b/test/CodeGen/arm-swiftcall.c @@ -0,0 +1,496 @@ +// RUN: %clang_cc1 -triple armv7-apple-darwin9 -emit-llvm -o - %s | FileCheck %s + +// This isn't really testing anything ARM-specific; it's just a convenient +// 32-bit platform. + +#define SWIFTCALL __attribute__((swiftcall)) +#define OUT __attribute__((swift_indirect_result)) +#define ERROR __attribute__((swift_error_result)) +#define CONTEXT __attribute__((swift_context)) + +/*****************************************************************************/ +/****************************** PARAMETER ABIS *******************************/ +/*****************************************************************************/ + +SWIFTCALL void indirect_result_1(OUT int *arg0, OUT float *arg1) {} +// CHECK-LABEL: define {{.*}} void @indirect_result_1(i32* noalias sret align 4 dereferenceable(4){{.*}}, float* noalias align 4 dereferenceable(4){{.*}}) + +// TODO: maybe this shouldn't suppress sret. +SWIFTCALL int indirect_result_2(OUT int *arg0, OUT float *arg1) { __builtin_unreachable(); } +// CHECK-LABEL: define {{.*}} i32 @indirect_result_2(i32* noalias align 4 dereferenceable(4){{.*}}, float* noalias align 4 dereferenceable(4){{.*}}) + +typedef struct { char array[1024]; } struct_reallybig; +SWIFTCALL struct_reallybig indirect_result_3(OUT int *arg0, OUT float *arg1) { __builtin_unreachable(); } +// CHECK-LABEL: define {{.*}} void @indirect_result_3({{.*}}* noalias sret {{.*}}, i32* noalias align 4 dereferenceable(4){{.*}}, float* noalias align 4 dereferenceable(4){{.*}}) + +SWIFTCALL void context_1(CONTEXT void *self) {} +// CHECK-LABEL: define {{.*}} void @context_1(i8* swiftself + +SWIFTCALL void context_2(void *arg0, CONTEXT void *self) {} +// CHECK-LABEL: define {{.*}} void @context_2(i8*{{.*}}, i8* swiftself + +SWIFTCALL void context_error_1(CONTEXT int *self, ERROR float **error) {} +// CHECK-LABEL: define {{.*}} void @context_error_1(i32* swiftself{{.*}}, float** swifterror) +// CHECK: [[TEMP:%.*]] = alloca float*, align 4 +// CHECK: [[T0:%.*]] = load float*, float** [[ERRORARG:%.*]], align 4 +// CHECK: store float* [[T0]], float** [[TEMP]], align 4 +// CHECK: [[T0:%.*]] = load float*, float** [[TEMP]], align 4 +// CHECK: store float* [[T0]], float** [[ERRORARG]], align 4 +void test_context_error_1() { + int x; + float *error; + context_error_1(&x, &error); +} +// CHECK-LABEL: define void @test_context_error_1() +// CHECK: [[X:%.*]] = alloca i32, align 4 +// CHECK: [[ERROR:%.*]] = alloca float*, align 4 +// CHECK: [[TEMP:%.*]] = alloca swifterror float*, align 4 +// CHECK: [[T0:%.*]] = load float*, float** [[ERROR]], align 4 +// CHECK: store float* [[T0]], float** [[TEMP]], align 4 +// CHECK: call [[SWIFTCC:swiftcc]] void @context_error_1(i32* swiftself [[X]], float** swifterror [[TEMP]]) +// CHECK: [[T0:%.*]] = load float*, float** [[TEMP]], align 4 +// CHECK: store float* [[T0]], float** [[ERROR]], align 4 + +SWIFTCALL void context_error_2(short s, CONTEXT int *self, ERROR float **error) {} +// CHECK-LABEL: define {{.*}} void @context_error_2(i16{{.*}}, i32* swiftself{{.*}}, float** swifterror) + +/*****************************************************************************/ +/********************************** LOWERING *********************************/ +/*****************************************************************************/ + +typedef float float4 __attribute__((ext_vector_type(4))); +typedef float float8 __attribute__((ext_vector_type(8))); +typedef double double2 __attribute__((ext_vector_type(2))); +typedef double double4 __attribute__((ext_vector_type(4))); +typedef int int4 __attribute__((ext_vector_type(4))); +typedef int int5 __attribute__((ext_vector_type(5))); +typedef int int8 __attribute__((ext_vector_type(8))); + +#define TEST(TYPE) \ + SWIFTCALL TYPE return_##TYPE(void) { \ + TYPE result = {}; \ + return result; \ + } \ + SWIFTCALL void take_##TYPE(TYPE v) { \ + } \ + void test_##TYPE() { \ + take_##TYPE(return_##TYPE()); \ + } + +/*****************************************************************************/ +/*********************************** STRUCTS *********************************/ +/*****************************************************************************/ + +typedef struct { +} struct_empty; +TEST(struct_empty); +// CHECK-LABEL: define {{.*}} @return_struct_empty() +// CHECK: ret void +// CHECK-LABEL: define {{.*}} @take_struct_empty() +// CHECK: ret void + +typedef struct { + int x; + char c0; + char c1; + float f0; + float f1; +} struct_1; +TEST(struct_1); +// CHECK-LABEL: define {{.*}} @return_struct_1() +// CHECK: [[RET:%.*]] = alloca [[REC:%.*]], align 4 +// CHECK: [[VAR:%.*]] = alloca [[REC]], align 4 +// CHECK: @llvm.memset +// CHECK: @llvm.memcpy +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[RET]] to [[AGG:{ i32, i16, \[2 x i8\], float, float }]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load i32, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[SECOND:%.*]] = load i16, i16* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 3 +// CHECK: [[THIRD:%.*]] = load float, float* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 4 +// CHECK: [[FOURTH:%.*]] = load float, float* [[T0]], align +// CHECK: [[T0:%.*]] = insertvalue [[UAGG:{ i32, i16, float, float }]] undef, i32 [[FIRST]], 0 +// CHECK: [[T1:%.*]] = insertvalue [[UAGG]] [[T0]], i16 [[SECOND]], 1 +// CHECK: [[T2:%.*]] = insertvalue [[UAGG]] [[T1]], float [[THIRD]], 2 +// CHECK: [[T3:%.*]] = insertvalue [[UAGG]] [[T2]], float [[FOURTH]], 3 +// CHECK: ret [[UAGG]] [[T3]] +// CHECK-LABEL: define {{.*}} @take_struct_1(i32, i16, float, float) +// CHECK: [[V:%.*]] = alloca [[REC]], align 4 +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[V]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: store i32 %0, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: store i16 %1, i16* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 3 +// CHECK: store float %2, float* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 4 +// CHECK: store float %3, float* [[T0]], align 4 +// CHECK: ret void +// CHECK-LABEL: define void @test_struct_1() +// CHECK: [[TMP:%.*]] = alloca [[REC]], align 4 +// CHECK: [[CALL:%.*]] = call [[SWIFTCC]] [[UAGG]] @return_struct_1() +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 0 +// CHECK: store i32 [[T1]], i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 1 +// CHECK: store i16 [[T1]], i16* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 3 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 2 +// CHECK: store float [[T1]], float* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 4 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 3 +// CHECK: store float [[T1]], float* [[T0]], align 4 +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load i32, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[SECOND:%.*]] = load i16, i16* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 3 +// CHECK: [[THIRD:%.*]] = load float, float* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 4 +// CHECK: [[FOURTH:%.*]] = load float, float* [[T0]], align 4 +// CHECK: call [[SWIFTCC]] void @take_struct_1(i32 [[FIRST]], i16 [[SECOND]], float [[THIRD]], float [[FOURTH]]) +// CHECK: ret void + +typedef struct { + int x; + char c0; + __attribute__((aligned(2))) char c1; + float f0; + float f1; +} struct_2; +TEST(struct_2); +// CHECK-LABEL: define {{.*}} @return_struct_2() +// CHECK: [[RET:%.*]] = alloca [[REC:%.*]], align 4 +// CHECK: [[VAR:%.*]] = alloca [[REC]], align 4 +// CHECK: @llvm.memcpy +// CHECK: @llvm.memcpy +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[RET]] to [[AGG:{ i32, i32, float, float }]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load i32, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[SECOND:%.*]] = load i32, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 2 +// CHECK: [[THIRD:%.*]] = load float, float* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 3 +// CHECK: [[FOURTH:%.*]] = load float, float* [[T0]], align +// CHECK: [[T0:%.*]] = insertvalue [[UAGG:{ i32, i32, float, float }]] undef, i32 [[FIRST]], 0 +// CHECK: [[T1:%.*]] = insertvalue [[UAGG]] [[T0]], i32 [[SECOND]], 1 +// CHECK: [[T2:%.*]] = insertvalue [[UAGG]] [[T1]], float [[THIRD]], 2 +// CHECK: [[T3:%.*]] = insertvalue [[UAGG]] [[T2]], float [[FOURTH]], 3 +// CHECK: ret [[UAGG]] [[T3]] +// CHECK-LABEL: define {{.*}} @take_struct_2(i32, i32, float, float) +// CHECK: [[V:%.*]] = alloca [[REC]], align 4 +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[V]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: store i32 %0, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: store i32 %1, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 2 +// CHECK: store float %2, float* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 3 +// CHECK: store float %3, float* [[T0]], align 4 +// CHECK: ret void +// CHECK-LABEL: define void @test_struct_2() +// CHECK: [[TMP:%.*]] = alloca [[REC]], align 4 +// CHECK: [[CALL:%.*]] = call [[SWIFTCC]] [[UAGG]] @return_struct_2() +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 0 +// CHECK: store i32 [[T1]], i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 1 +// CHECK: store i32 [[T1]], i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 2 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 2 +// CHECK: store float [[T1]], float* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 3 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 3 +// CHECK: store float [[T1]], float* [[T0]], align 4 +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load i32, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[SECOND:%.*]] = load i32, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 2 +// CHECK: [[THIRD:%.*]] = load float, float* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 3 +// CHECK: [[FOURTH:%.*]] = load float, float* [[T0]], align 4 +// CHECK: call [[SWIFTCC]] void @take_struct_2(i32 [[FIRST]], i32 [[SECOND]], float [[THIRD]], float [[FOURTH]]) +// CHECK: ret void + +// There's no way to put a field randomly in the middle of an otherwise +// empty storage unit in C, so that case has to be tested in C++, which +// can use empty structs to introduce arbitrary padding. (In C, they end up +// with size 0 and so don't affect layout.) + +// Misaligned data rule. +typedef struct { + char c0; + __attribute__((packed)) float f; +} struct_misaligned_1; +TEST(struct_misaligned_1) +// CHECK-LABEL: define {{.*}} @return_struct_misaligned_1() +// CHECK: [[RET:%.*]] = alloca [[REC:%.*]], align +// CHECK: [[VAR:%.*]] = alloca [[REC]], align +// CHECK: @llvm.memset +// CHECK: @llvm.memcpy +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[RET]] to [[AGG:{ i32, i8 }]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load i32, i32* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[SECOND:%.*]] = load i8, i8* [[T0]], align +// CHECK: [[T0:%.*]] = insertvalue [[UAGG:{ i32, i8 }]] undef, i32 [[FIRST]], 0 +// CHECK: [[T1:%.*]] = insertvalue [[UAGG]] [[T0]], i8 [[SECOND]], 1 +// CHECK: ret [[UAGG]] [[T1]] +// CHECK-LABEL: define {{.*}} @take_struct_misaligned_1(i32, i8) +// CHECK: [[V:%.*]] = alloca [[REC]], align +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[V]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: store i32 %0, i32* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: store i8 %1, i8* [[T0]], align +// CHECK: ret void + +// Too many scalars. +typedef struct { + int x[5]; +} struct_big_1; +TEST(struct_big_1) + +// CHECK-LABEL: define {{.*}} void @return_struct_big_1({{.*}} noalias sret + +// Should not be byval. +// CHECK-LABEL: define {{.*}} void @take_struct_big_1({{.*}}*{{( %.*)?}}) + +/*****************************************************************************/ +/********************************* TYPE MERGING ******************************/ +/*****************************************************************************/ + +typedef union { + float f; + double d; +} union_het_fp; +TEST(union_het_fp) +// CHECK-LABEL: define {{.*}} @return_union_het_fp() +// CHECK: [[RET:%.*]] = alloca [[REC:%.*]], align 4 +// CHECK: [[VAR:%.*]] = alloca [[REC]], align 4 +// CHECK: @llvm.memcpy +// CHECK: @llvm.memcpy +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[RET]] to [[AGG:{ i32, i32 }]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load i32, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[SECOND:%.*]] = load i32, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = insertvalue [[UAGG:{ i32, i32 }]] undef, i32 [[FIRST]], 0 +// CHECK: [[T1:%.*]] = insertvalue [[UAGG]] [[T0]], i32 [[SECOND]], 1 +// CHECK: ret [[UAGG]] [[T1]] +// CHECK-LABEL: define {{.*}} @take_union_het_fp(i32, i32) +// CHECK: [[V:%.*]] = alloca [[REC]], align 4 +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[V]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: store i32 %0, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: store i32 %1, i32* [[T0]], align 4 +// CHECK: ret void +// CHECK-LABEL: define void @test_union_het_fp() +// CHECK: [[TMP:%.*]] = alloca [[REC]], align 4 +// CHECK: [[CALL:%.*]] = call [[SWIFTCC]] [[UAGG]] @return_union_het_fp() +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 0 +// CHECK: store i32 [[T1]], i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 1 +// CHECK: store i32 [[T1]], i32* [[T0]], align 4 +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load i32, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[SECOND:%.*]] = load i32, i32* [[T0]], align 4 +// CHECK: call [[SWIFTCC]] void @take_union_het_fp(i32 [[FIRST]], i32 [[SECOND]]) +// CHECK: ret void + + +typedef union { + float f1; + float f2; +} union_hom_fp; +TEST(union_hom_fp) +// CHECK-LABEL: define void @test_union_hom_fp() +// CHECK: [[TMP:%.*]] = alloca [[REC:%.*]], align 4 +// CHECK: [[CALL:%.*]] = call [[SWIFTCC]] float @return_union_hom_fp() +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP]] to [[AGG:{ float }]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: store float [[CALL]], float* [[T0]], align 4 +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load float, float* [[T0]], align 4 +// CHECK: call [[SWIFTCC]] void @take_union_hom_fp(float [[FIRST]]) +// CHECK: ret void + +typedef union { + float f1; + float4 fv2; +} union_hom_fp_partial; +TEST(union_hom_fp_partial) +// CHECK-LABEL: define void @test_union_hom_fp_partial() +// CHECK: [[TMP:%.*]] = alloca [[REC:%.*]], align 16 +// CHECK: [[CALL:%.*]] = call [[SWIFTCC]] [[UAGG:{ float, float, float, float }]] @return_union_hom_fp_partial() +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP]] to [[AGG:{ float, float, float, float }]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 0 +// CHECK: store float [[T1]], float* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 1 +// CHECK: store float [[T1]], float* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 2 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 2 +// CHECK: store float [[T1]], float* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 3 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 3 +// CHECK: store float [[T1]], float* [[T0]], align +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load float, float* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[SECOND:%.*]] = load float, float* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 2 +// CHECK: [[THIRD:%.*]] = load float, float* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 3 +// CHECK: [[FOURTH:%.*]] = load float, float* [[T0]], align +// CHECK: call [[SWIFTCC]] void @take_union_hom_fp_partial(float [[FIRST]], float [[SECOND]], float [[THIRD]], float [[FOURTH]]) +// CHECK: ret void + +typedef union { + struct { int x, y; } f1; + float4 fv2; +} union_het_fpv_partial; +TEST(union_het_fpv_partial) +// CHECK-LABEL: define void @test_union_het_fpv_partial() +// CHECK: [[TMP:%.*]] = alloca [[REC:%.*]], align 16 +// CHECK: [[CALL:%.*]] = call [[SWIFTCC]] [[UAGG:{ i32, i32, float, float }]] @return_union_het_fpv_partial() +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP]] to [[AGG:{ i32, i32, float, float }]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 0 +// CHECK: store i32 [[T1]], i32* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 1 +// CHECK: store i32 [[T1]], i32* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 2 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 2 +// CHECK: store float [[T1]], float* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 3 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 3 +// CHECK: store float [[T1]], float* [[T0]], align +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load i32, i32* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[SECOND:%.*]] = load i32, i32* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 2 +// CHECK: [[THIRD:%.*]] = load float, float* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 3 +// CHECK: [[FOURTH:%.*]] = load float, float* [[T0]], align +// CHECK: call [[SWIFTCC]] void @take_union_het_fpv_partial(i32 [[FIRST]], i32 [[SECOND]], float [[THIRD]], float [[FOURTH]]) +// CHECK: ret void + +/*****************************************************************************/ +/****************************** VECTOR LEGALIZATION **************************/ +/*****************************************************************************/ + +TEST(int4) +// CHECK-LABEL: define {{.*}} <4 x i32> @return_int4() +// CHECK-LABEL: define {{.*}} @take_int4(<4 x i32> + +TEST(int8) +// CHECK-LABEL: define {{.*}} @return_int8() +// CHECK: [[RET:%.*]] = alloca [[REC:<8 x i32>]], align 32 +// CHECK: [[VAR:%.*]] = alloca [[REC]], align +// CHECK: store +// CHECK: load +// CHECK: store +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[RET]] to [[AGG:{ <4 x i32>, <4 x i32> }]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load <4 x i32>, <4 x i32>* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[SECOND:%.*]] = load <4 x i32>, <4 x i32>* [[T0]], align +// CHECK: [[T0:%.*]] = insertvalue [[UAGG:{ <4 x i32>, <4 x i32> }]] undef, <4 x i32> [[FIRST]], 0 +// CHECK: [[T1:%.*]] = insertvalue [[UAGG]] [[T0]], <4 x i32> [[SECOND]], 1 +// CHECK: ret [[UAGG]] [[T1]] +// CHECK-LABEL: define {{.*}} @take_int8(<4 x i32>, <4 x i32>) +// CHECK: [[V:%.*]] = alloca [[REC]], align +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[V]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: store <4 x i32> %0, <4 x i32>* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: store <4 x i32> %1, <4 x i32>* [[T0]], align +// CHECK: ret void +// CHECK-LABEL: define void @test_int8() +// CHECK: [[TMP1:%.*]] = alloca [[REC]], align +// CHECK: [[TMP2:%.*]] = alloca [[REC]], align +// CHECK: [[CALL:%.*]] = call [[SWIFTCC]] [[UAGG]] @return_int8() +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP1]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 0 +// CHECK: store <4 x i32> [[T1]], <4 x i32>* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 1 +// CHECK: store <4 x i32> [[T1]], <4 x i32>* [[T0]], align +// CHECK: [[V:%.*]] = load [[REC]], [[REC]]* [[TMP1]], align +// CHECK: store [[REC]] [[V]], [[REC]]* [[TMP2]], align +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP2]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load <4 x i32>, <4 x i32>* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[SECOND:%.*]] = load <4 x i32>, <4 x i32>* [[T0]], align +// CHECK: call [[SWIFTCC]] void @take_int8(<4 x i32> [[FIRST]], <4 x i32> [[SECOND]]) +// CHECK: ret void + +TEST(int5) +// CHECK-LABEL: define {{.*}} @return_int5() +// CHECK: [[RET:%.*]] = alloca [[REC:<5 x i32>]], align 32 +// CHECK: [[VAR:%.*]] = alloca [[REC]], align +// CHECK: store +// CHECK: load +// CHECK: store +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[RET]] to [[AGG:{ <4 x i32>, i32 }]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load <4 x i32>, <4 x i32>* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[SECOND:%.*]] = load i32, i32* [[T0]], align +// CHECK: [[T0:%.*]] = insertvalue [[UAGG:{ <4 x i32>, i32 }]] undef, <4 x i32> [[FIRST]], 0 +// CHECK: [[T1:%.*]] = insertvalue [[UAGG]] [[T0]], i32 [[SECOND]], 1 +// CHECK: ret [[UAGG]] [[T1]] +// CHECK-LABEL: define {{.*}} @take_int5(<4 x i32>, i32) +// CHECK: [[V:%.*]] = alloca [[REC]], align +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[V]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: store <4 x i32> %0, <4 x i32>* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: store i32 %1, i32* [[T0]], align +// CHECK: ret void +// CHECK-LABEL: define void @test_int5() +// CHECK: [[TMP1:%.*]] = alloca [[REC]], align +// CHECK: [[TMP2:%.*]] = alloca [[REC]], align +// CHECK: [[CALL:%.*]] = call [[SWIFTCC]] [[UAGG]] @return_int5() +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP1]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 0 +// CHECK: store <4 x i32> [[T1]], <4 x i32>* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 1 +// CHECK: store i32 [[T1]], i32* [[T0]], align +// CHECK: [[V:%.*]] = load [[REC]], [[REC]]* [[TMP1]], align +// CHECK: store [[REC]] [[V]], [[REC]]* [[TMP2]], align +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP2]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load <4 x i32>, <4 x i32>* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[SECOND:%.*]] = load i32, i32* [[T0]], align +// CHECK: call [[SWIFTCC]] void @take_int5(<4 x i32> [[FIRST]], i32 [[SECOND]]) +// CHECK: ret void diff --git a/test/CodeGenCXX/arm-swiftcall.cpp b/test/CodeGenCXX/arm-swiftcall.cpp new file mode 100644 index 00000000000..535350c808d --- /dev/null +++ b/test/CodeGenCXX/arm-swiftcall.cpp @@ -0,0 +1,115 @@ +// RUN: %clang_cc1 -triple armv7-apple-darwin9 -emit-llvm -o - %s -Wno-return-type-c-linkage | FileCheck %s + +// This isn't really testing anything ARM-specific; it's just a convenient +// 32-bit platform. + +#define SWIFTCALL __attribute__((swiftcall)) +#define OUT __attribute__((swift_indirect_result)) +#define ERROR __attribute__((swift_error_result)) +#define CONTEXT __attribute__((swift_context)) + +/*****************************************************************************/ +/********************************** LOWERING *********************************/ +/*****************************************************************************/ + +#define TEST(TYPE) \ + extern "C" SWIFTCALL TYPE return_##TYPE(void) { \ + TYPE result = {}; \ + return result; \ + } \ + extern "C" SWIFTCALL void take_##TYPE(TYPE v) { \ + } \ + extern "C" void test_##TYPE() { \ + take_##TYPE(return_##TYPE()); \ + } + +/*****************************************************************************/ +/*********************************** STRUCTS *********************************/ +/*****************************************************************************/ + +typedef struct { +} struct_empty; +TEST(struct_empty); +// CHECK-LABEL: define {{.*}} @return_struct_empty() +// CHECK: ret void +// CHECK-LABEL: define {{.*}} @take_struct_empty() +// CHECK: ret void + +// This is only properly testable in C++ because it relies on empty structs +// actually taking up space in a structure without requiring any extra data +// to be passed. +typedef struct { + int x; + struct_empty padding[2]; + char c1; + float f0; + float f1; +} struct_1; +TEST(struct_1); +// CHECK-LABEL: define {{.*}} @return_struct_1() +// CHECK: [[RET:%.*]] = alloca [[REC:%.*]], align 4 +// CHECK: @llvm.memset +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[RET]] to [[AGG:{ i32, \[2 x i8\], i8, \[1 x i8\], float, float }]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load i32, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 2 +// CHECK: [[SECOND:%.*]] = load i8, i8* [[T0]], align 2 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 4 +// CHECK: [[THIRD:%.*]] = load float, float* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 5 +// CHECK: [[FOURTH:%.*]] = load float, float* [[T0]], align 4 +// CHECK: [[T0:%.*]] = insertvalue [[UAGG:{ i32, i8, float, float }]] undef, i32 [[FIRST]], 0 +// CHECK: [[T1:%.*]] = insertvalue [[UAGG]] [[T0]], i8 [[SECOND]], 1 +// CHECK: [[T2:%.*]] = insertvalue [[UAGG]] [[T1]], float [[THIRD]], 2 +// CHECK: [[T3:%.*]] = insertvalue [[UAGG]] [[T2]], float [[FOURTH]], 3 +// CHECK: ret [[UAGG]] [[T3]] +// CHECK-LABEL: define {{.*}} @take_struct_1(i32, i8, float, float) +// CHECK: [[V:%.*]] = alloca [[REC]], align 4 +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[V]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: store i32 %0, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 2 +// CHECK: store i8 %1, i8* [[T0]], align 2 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 4 +// CHECK: store float %2, float* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 5 +// CHECK: store float %3, float* [[T0]], align 4 +// CHECK: ret void +// CHECK-LABEL: define void @test_struct_1() +// CHECK: [[TMP:%.*]] = alloca [[REC]], align 4 +// CHECK: [[CALL:%.*]] = call [[SWIFTCC:swiftcc]] [[UAGG]] @return_struct_1() +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 0 +// CHECK: store i32 [[T1]], i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 2 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 1 +// CHECK: store i8 [[T1]], i8* [[T0]], align 2 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 4 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 2 +// CHECK: store float [[T1]], float* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 5 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 3 +// CHECK: store float [[T1]], float* [[T0]], align 4 +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load i32, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 2 +// CHECK: [[SECOND:%.*]] = load i8, i8* [[T0]], align 2 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 4 +// CHECK: [[THIRD:%.*]] = load float, float* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 5 +// CHECK: [[FOURTH:%.*]] = load float, float* [[T0]], align 4 +// CHECK: call [[SWIFTCC]] void @take_struct_1(i32 [[FIRST]], i8 [[SECOND]], float [[THIRD]], float [[FOURTH]]) +// CHECK: ret void + +struct struct_indirect_1 { + int x; + ~struct_indirect_1(); +}; +TEST(struct_indirect_1) + +// CHECK-LABEL: define {{.*}} void @return_struct_indirect_1({{.*}} noalias sret + +// Should not be byval. +// CHECK-LABEL: define {{.*}} void @take_struct_indirect_1({{.*}}*{{( %.*)?}}) From 9aee8f0e19275a94d44652a73233414ab8881830 Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 4 Apr 2016 18:53:01 +0000 Subject: [PATCH 431/742] Assignment operators should return by reference. Thanks to Sean Silva for pointing this out. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265328 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/CharUnits.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/clang/AST/CharUnits.h b/include/clang/AST/CharUnits.h index b2da51c23b8..564c8ec9b9e 100644 --- a/include/clang/AST/CharUnits.h +++ b/include/clang/AST/CharUnits.h @@ -149,7 +149,7 @@ namespace clang { CharUnits operator/ (QuantityType N) const { return CharUnits(Quantity / N); } - CharUnits operator/= (QuantityType N) { + CharUnits &operator/= (QuantityType N) { Quantity /= N; return *this; } From fa83172882a6fb62b89c8172bbbfe6bc71e6baf1 Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 4 Apr 2016 20:39:50 +0000 Subject: [PATCH 432/742] Fix an unused-variable warning by using the variable in the place it was supposed to have been used. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265344 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/SwiftCallingConv.cpp | 2 +- test/CodeGen/arm-swiftcall.c | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/CodeGen/SwiftCallingConv.cpp b/lib/CodeGen/SwiftCallingConv.cpp index 6fae19f2779..44b46f62f86 100644 --- a/lib/CodeGen/SwiftCallingConv.cpp +++ b/lib/CodeGen/SwiftCallingConv.cpp @@ -239,7 +239,7 @@ void SwiftAggLowering::addLegalTypedData(llvm::Type *type, auto eltSize = (end - begin) / numElts; assert(eltSize == getTypeStoreSize(CGM, eltTy)); for (size_t i = 0, e = numElts; i != e; ++i) { - addLegalTypedData(type, begin, begin + eltSize); + addLegalTypedData(eltTy, begin, begin + eltSize); begin += eltSize; } assert(begin == end); diff --git a/test/CodeGen/arm-swiftcall.c b/test/CodeGen/arm-swiftcall.c index dc1d68e34b9..d54a3133708 100644 --- a/test/CodeGen/arm-swiftcall.c +++ b/test/CodeGen/arm-swiftcall.c @@ -62,6 +62,7 @@ typedef float float4 __attribute__((ext_vector_type(4))); typedef float float8 __attribute__((ext_vector_type(8))); typedef double double2 __attribute__((ext_vector_type(2))); typedef double double4 __attribute__((ext_vector_type(4))); +typedef int int3 __attribute__((ext_vector_type(3))); typedef int int4 __attribute__((ext_vector_type(4))); typedef int int5 __attribute__((ext_vector_type(5))); typedef int int8 __attribute__((ext_vector_type(8))); @@ -494,3 +495,10 @@ TEST(int5) // CHECK: [[SECOND:%.*]] = load i32, i32* [[T0]], align // CHECK: call [[SWIFTCC]] void @take_int5(<4 x i32> [[FIRST]], i32 [[SECOND]]) // CHECK: ret void + +typedef struct { + int x; + int3 v __attribute__((packed)); +} misaligned_int3; +TEST(misaligned_int3) +// CHECK-LABEL: define {{.*}} @take_misaligned_int3(i32, i32, i32, i32) From 8b35bc4f85cbbcadd1c4230d91d3e326b25e8bfc Mon Sep 17 00:00:00 2001 From: Benjamin Kramer Date: Fri, 1 Apr 2016 09:58:45 +0000 Subject: [PATCH 433/742] [Lexer] Don't read out of bounds if a conflict marker is at the end of a file This can happen as we look for '<<<<' while scanning tokens but then expect '<<<<\n' to tell apart perforce from diff3 conflict markers. Just harden the pointer arithmetic. Found by libfuzzer + asan! git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265125 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 3eb041dcd1a89e1aa71edbb87034dae99b52e63c) --- lib/Lex/Lexer.cpp | 2 +- test/Lexer/eof-conflict-marker.c | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 test/Lexer/eof-conflict-marker.c diff --git a/lib/Lex/Lexer.cpp b/lib/Lex/Lexer.cpp index 27b0feb4827..3cdd452ad35 100644 --- a/lib/Lex/Lexer.cpp +++ b/lib/Lex/Lexer.cpp @@ -2582,7 +2582,7 @@ static const char *FindConflictEnd(const char *CurPtr, const char *BufferEnd, ConflictMarkerKind CMK) { const char *Terminator = CMK == CMK_Perforce ? "<<<<\n" : ">>>>>>>"; size_t TermLen = CMK == CMK_Perforce ? 5 : 7; - StringRef RestOfBuffer(CurPtr+TermLen, BufferEnd-CurPtr-TermLen); + auto RestOfBuffer = StringRef(CurPtr, BufferEnd - CurPtr).substr(TermLen); size_t Pos = RestOfBuffer.find(Terminator); while (Pos != StringRef::npos) { // Must occur at start of line. diff --git a/test/Lexer/eof-conflict-marker.c b/test/Lexer/eof-conflict-marker.c new file mode 100644 index 00000000000..e0c35401ccb --- /dev/null +++ b/test/Lexer/eof-conflict-marker.c @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 %s -verify -fsyntax-only +// vim: set binary noeol: + +// This file intentionally ends without a \n on the last line. Make sure your +// editor doesn't add one. + +>>>> ORIGINAL +// expected-error@-1 {{version control conflict marker in file}} +<<<< +// expected-error@-1 {{expected identifier or '('}} +<<<< \ No newline at end of file From 4acd64efa09486d5e2f1b293382ec9942d4755d5 Mon Sep 17 00:00:00 2001 From: Yunzhong Gao Date: Mon, 4 Apr 2016 18:46:09 +0000 Subject: [PATCH 434/742] Add copyright notice to the modulemap file. The module.modulemap file in the lib/Headers directory was missing the LLVM copyright notice. This patch adds the copyright notice just like the rest of the files in this directory. Differential Revision: http://reviews.llvm.org/D18709 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265325 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 7c9014b9917e33940af9650203915f8b56fb44ac) --- lib/Headers/module.modulemap | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lib/Headers/module.modulemap b/lib/Headers/module.modulemap index b147e891dce..e0b790b84b7 100644 --- a/lib/Headers/module.modulemap +++ b/lib/Headers/module.modulemap @@ -1,3 +1,26 @@ +/*===---- module.modulemap - intrinsics module map -------------------------=== + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + *===-----------------------------------------------------------------------=== + */ + module _Builtin_intrinsics [system] [extern_c] { explicit module altivec { requires altivec From f816e004c82df335f2cdf270d4366906c80c75ff Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Wed, 6 Apr 2016 13:02:52 -0700 Subject: [PATCH 435/742] [Import as member] Default initialize ModuleOptions fields --- include/clang/APINotes/Types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/clang/APINotes/Types.h b/include/clang/APINotes/Types.h index 7b4d0647ef2..b19eea51991 100644 --- a/include/clang/APINotes/Types.h +++ b/include/clang/APINotes/Types.h @@ -476,7 +476,7 @@ class TypedefInfo : public CommonTypeInfo { /// Descripts a series of options for a module struct ModuleOptions { - bool SwiftInferImportAsMember; + bool SwiftInferImportAsMember = false; }; } // end namespace api_notes From 7f246f71f58aac4354b361809266586b8a68f478 Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Wed, 6 Apr 2016 13:07:47 -0700 Subject: [PATCH 436/742] [Import as member] Fix my screw up --- lib/APINotes/APINotesYAMLCompiler.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/APINotes/APINotesYAMLCompiler.cpp b/lib/APINotes/APINotesYAMLCompiler.cpp index a9c21535c8e..9d7873cee6d 100644 --- a/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/lib/APINotes/APINotesYAMLCompiler.cpp @@ -250,7 +250,7 @@ namespace { TagsSeq Tags; TypedefsSeq Typedefs; - llvm::Optional SwiftInferImportAsMember; + llvm::Optional SwiftInferImportAsMember = {llvm::None}; LLVM_ATTRIBUTE_DEPRECATED( void dump() LLVM_ATTRIBUTE_USED, @@ -774,8 +774,11 @@ namespace { Writer->addTypedef(t.Name, typedefInfo); } - if (TheModule.SwiftInferImportAsMember) - Writer->addModuleOptions({true}); + if (TheModule.SwiftInferImportAsMember) { + ModuleOptions opts; + opts.SwiftInferImportAsMember = true; + Writer->addModuleOptions(opts); + } if (!ErrorOccured) Writer->writeToStream(OS); From 9a2153a54d0d001062b6ad5b877adcd730a0f9ab Mon Sep 17 00:00:00 2001 From: Tim Northover Date: Wed, 6 Apr 2016 19:58:07 +0000 Subject: [PATCH 437/742] Restore slightly less dodgy diagnostic handler for inline asm Turns out it was there mostly to prevent Clang asking people to report a bug. This time we report something to Clang's real diagnostics handler so that it exits with something approximating a real error and tidies up after itself. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265592 91177308-0d34-0410-b5e6-96231b3b80d8 Conflicts: test/CodeGen/asm-errors.c --- lib/CodeGen/CodeGenAction.cpp | 25 +++++++++++++++++++++++++ test/CodeGen/asm-errors.c | 1 + 2 files changed, 26 insertions(+) diff --git a/lib/CodeGen/CodeGenAction.cpp b/lib/CodeGen/CodeGenAction.cpp index 1b4ed23f974..bcf671b64a0 100644 --- a/lib/CodeGen/CodeGenAction.cpp +++ b/lib/CodeGen/CodeGenAction.cpp @@ -725,6 +725,28 @@ CodeGenAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { return std::move(Result); } +static void BitcodeInlineAsmDiagHandler(const llvm::SMDiagnostic &SM, + void *Context, + unsigned LocCookie) { + SM.print(nullptr, llvm::errs()); + + auto Diags = static_cast(Context); + unsigned DiagID; + switch (SM.getKind()) { + case llvm::SourceMgr::DK_Error: + DiagID = diag::err_fe_inline_asm; + break; + case llvm::SourceMgr::DK_Warning: + DiagID = diag::warn_fe_inline_asm; + break; + case llvm::SourceMgr::DK_Note: + DiagID = diag::note_fe_inline_asm; + break; + } + + Diags->Report(DiagID).AddString("cannot compile inline asm"); +} + void CodeGenAction::ExecuteAction() { // If this is an IR file, we have to treat it specially. if (getCurrentFileKind() == IK_LLVM_IR) { @@ -773,6 +795,9 @@ void CodeGenAction::ExecuteAction() { TheModule->setTargetTriple(TargetOpts.Triple); } + LLVMContext &Ctx = TheModule->getContext(); + Ctx.setInlineAsmDiagnosticHandler(BitcodeInlineAsmDiagHandler, + &CI.getDiagnostics()); EmitBackendOutput(CI.getDiagnostics(), CI.getCodeGenOpts(), TargetOpts, CI.getLangOpts(), CI.getTarget().getDataLayoutString(), TheModule.get(), BA, OS); diff --git a/test/CodeGen/asm-errors.c b/test/CodeGen/asm-errors.c index 9acb693d581..ed7b02b919d 100644 --- a/test/CodeGen/asm-errors.c +++ b/test/CodeGen/asm-errors.c @@ -8,6 +8,7 @@ // RUN: FileCheck --check-prefix=CRASH-REPORT %s // CRASH-REPORT: : // CRASH-REPORT: error: invalid instruction mnemonic 'abc' +// CRASH-REPORT: error: cannot compile inline asm // CRASH-REPORT-NOT: note: diagnostic msg: int test1(int X) { From 52613ac518dae0fbccb24e5e0b2c2b3e1b13d845 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 5 Apr 2016 09:45:49 -0700 Subject: [PATCH 438/742] Ignore redundant, implicit swift_name attributes. --- lib/Sema/SemaDeclAttr.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 41d68edcc95..ecc744c44b8 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -3612,8 +3612,12 @@ SwiftNameAttr *Sema::mergeSwiftNameAttr(Decl *D, SourceRange Range, // FIXME: Warn about an incompatible override. return nullptr; } - Diag(Inline->getLocation(), diag::warn_attribute_ignored) << Inline; - Diag(Range.getBegin(), diag::note_conflicting_attribute); + + if (Inline->getName() != Name && !Inline->isImplicit()) { + Diag(Inline->getLocation(), diag::warn_attribute_ignored) << Inline; + Diag(Range.getBegin(), diag::note_conflicting_attribute); + } + D->dropAttr(); } From 2b97aca604d28197095e81c929245977358d3889 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 5 Apr 2016 09:47:08 -0700 Subject: [PATCH 439/742] [API notes] Make sure to remove our module caches before running tests. --- test/APINotes/nullability.c | 1 + test/APINotes/nullability.m | 1 + test/APINotes/objc_designated_inits.m | 1 + 3 files changed, 3 insertions(+) diff --git a/test/APINotes/nullability.c b/test/APINotes/nullability.c index 54b0df6cea9..86054eade82 100644 --- a/test/APINotes/nullability.c +++ b/test/APINotes/nullability.c @@ -1,3 +1,4 @@ +// RUN: rm -rf %t && mkdir -p %t // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify #include "HeaderLib.h" diff --git a/test/APINotes/nullability.m b/test/APINotes/nullability.m index ba51cd64d82..ce4beabb597 100644 --- a/test/APINotes/nullability.m +++ b/test/APINotes/nullability.m @@ -1,3 +1,4 @@ +// RUN: rm -rf %t && mkdir -p %t // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify #import diff --git a/test/APINotes/objc_designated_inits.m b/test/APINotes/objc_designated_inits.m index 5eb5fa103d5..bbb50ba1a74 100644 --- a/test/APINotes/objc_designated_inits.m +++ b/test/APINotes/objc_designated_inits.m @@ -1,3 +1,4 @@ +// RUN: rm -rf %t && mkdir -p %t // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify #include "HeaderLib.h" From f2eb8a25850780762ab8e76e66b77482306838ab Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 5 Apr 2016 09:45:49 -0700 Subject: [PATCH 440/742] Ignore redundant, implicit swift_name attributes. --- lib/Sema/SemaDeclAttr.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index a4911815f5b..22a5fae572f 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -3625,8 +3625,12 @@ SwiftNameAttr *Sema::mergeSwiftNameAttr(Decl *D, SourceRange Range, // FIXME: Warn about an incompatible override. return nullptr; } - Diag(Inline->getLocation(), diag::warn_attribute_ignored) << Inline; - Diag(Range.getBegin(), diag::note_conflicting_attribute); + + if (Inline->getName() != Name && !Inline->isImplicit()) { + Diag(Inline->getLocation(), diag::warn_attribute_ignored) << Inline; + Diag(Range.getBegin(), diag::note_conflicting_attribute); + } + D->dropAttr(); } From f47f7359753c2405f557e90bd7ba802142315fea Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 5 Apr 2016 09:47:08 -0700 Subject: [PATCH 441/742] [API notes] Make sure to remove our module caches before running tests. --- test/APINotes/nullability.c | 1 + test/APINotes/nullability.m | 1 + test/APINotes/objc_designated_inits.m | 1 + 3 files changed, 3 insertions(+) diff --git a/test/APINotes/nullability.c b/test/APINotes/nullability.c index 54b0df6cea9..86054eade82 100644 --- a/test/APINotes/nullability.c +++ b/test/APINotes/nullability.c @@ -1,3 +1,4 @@ +// RUN: rm -rf %t && mkdir -p %t // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify #include "HeaderLib.h" diff --git a/test/APINotes/nullability.m b/test/APINotes/nullability.m index ba51cd64d82..ce4beabb597 100644 --- a/test/APINotes/nullability.m +++ b/test/APINotes/nullability.m @@ -1,3 +1,4 @@ +// RUN: rm -rf %t && mkdir -p %t // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify #import diff --git a/test/APINotes/objc_designated_inits.m b/test/APINotes/objc_designated_inits.m index 5eb5fa103d5..bbb50ba1a74 100644 --- a/test/APINotes/objc_designated_inits.m +++ b/test/APINotes/objc_designated_inits.m @@ -1,3 +1,4 @@ +// RUN: rm -rf %t && mkdir -p %t // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify #include "HeaderLib.h" From 38d1bfb97b6d95abb1af5485439f73c3b99cef34 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Fri, 19 Feb 2016 22:25:36 +0000 Subject: [PATCH 442/742] [modules] Flatten -fmodule-name= and -fmodule-implementation-of= into a single option. Previously these options could both be used to specify that you were compiling the implementation file of a module, with a different set of minor bugs in each case. This change removes -fmodule-implementation-of, and instead tracks a flag to determine whether we're currently building a module. -fmodule-name now behaves the same way that -fmodule-implementation-of previously did. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261372 91177308-0d34-0410-b5e6-96231b3b80d8 Conflicts: include/clang/Lex/ModuleMap.h lib/Lex/ModuleMap.cpp --- .../clang/Basic/DiagnosticFrontendKinds.td | 4 -- include/clang/Basic/LangOptions.def | 1 + include/clang/Basic/LangOptions.h | 10 ++--- include/clang/Driver/CC1Options.td | 3 -- include/clang/Driver/Options.td | 3 +- include/clang/Lex/ModuleMap.h | 9 +---- lib/Basic/LangOptions.cpp | 1 - lib/Driver/Tools.cpp | 2 +- lib/Frontend/ASTUnit.cpp | 2 +- lib/Frontend/CompilerInstance.cpp | 10 +---- lib/Frontend/CompilerInvocation.cpp | 10 +---- lib/Frontend/FrontendActions.cpp | 2 + lib/Lex/ModuleMap.cpp | 29 ++++++--------- lib/Lex/PPDirectives.cpp | 37 +++++++++---------- lib/Lex/PPMacroExpansion.cpp | 5 ++- lib/Lex/Preprocessor.cpp | 2 +- lib/Sema/SemaDecl.cpp | 19 +++++----- lib/Sema/SemaDeclObjC.cpp | 2 +- test/Modules/Inputs/explicit-build/a.h | 2 +- test/Modules/explicit-build.cpp | 2 +- test/Modules/implementation-of-module.m | 14 +++---- test/Modules/import-self.m | 2 +- 22 files changed, 67 insertions(+), 104 deletions(-) diff --git a/include/clang/Basic/DiagnosticFrontendKinds.td b/include/clang/Basic/DiagnosticFrontendKinds.td index 7dcf697306f..170b53f53fe 100644 --- a/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/include/clang/Basic/DiagnosticFrontendKinds.td @@ -208,10 +208,6 @@ def err_test_module_file_extension_version : Error< "test module file extension '%0' has different version (%1.%2) than expected " "(%3.%4)">; -def err_conflicting_module_names : Error< - "conflicting module names specified: '-fmodule-name=%0' and " - "'-fmodule-implementation-of %1'">; - def err_missing_vfs_overlay_file : Error< "virtual filesystem overlay file '%0' not found">, DefaultFatal; def err_invalid_vfs_overlay : Error< diff --git a/include/clang/Basic/LangOptions.def b/include/clang/Basic/LangOptions.def index 594c8c770bc..1a6c72f2a47 100644 --- a/include/clang/Basic/LangOptions.def +++ b/include/clang/Basic/LangOptions.def @@ -127,6 +127,7 @@ BENIGN_LANGOPT(EmitAllDecls , 1, 0, "support for emitting all declarations" LANGOPT(MathErrno , 1, 1, "errno support for math functions") BENIGN_LANGOPT(HeinousExtensions , 1, 0, "Extensions that we really don't like and may be ripped out at any time") LANGOPT(Modules , 1, 0, "modules extension to C") +BENIGN_LANGOPT(CompilingModule, 1, 0, "compiling a module interface") COMPATIBLE_LANGOPT(ModulesDeclUse , 1, 0, "require declaration of module uses") LANGOPT(ModulesSearchAll , 1, 1, "search even non-imported modules to find unresolved references") COMPATIBLE_LANGOPT(ModulesStrictDeclUse, 1, 0, "require declaration of module uses and all headers to be in modules") diff --git a/include/clang/Basic/LangOptions.h b/include/clang/Basic/LangOptions.h index 736d4e0930b..a069a8f8b8f 100644 --- a/include/clang/Basic/LangOptions.h +++ b/include/clang/Basic/LangOptions.h @@ -92,14 +92,12 @@ class LangOptions : public LangOptionsBase { /// If none is specified, abort (GCC-compatible behaviour). std::string OverflowHandler; - /// \brief The name of the current module. + /// \brief The name of the current module, of which the main source file + /// is a part. If CompilingModule is set, we are compiling the interface + /// of this module, otherwise we are compiling an implementation file of + /// it. std::string CurrentModule; - /// \brief The name of the module that the translation unit is an - /// implementation of. Prevents semantic imports, but does not otherwise - /// treat this as the CurrentModule. - std::string ImplementationOfModule; - /// \brief The names of any features to enable in module 'requires' decls /// in addition to the hard-coded list in Module.cpp and the target features. /// diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td index be333c3c1e7..8702ad506c8 100644 --- a/include/clang/Driver/CC1Options.td +++ b/include/clang/Driver/CC1Options.td @@ -373,9 +373,6 @@ def fno_modules_global_index : Flag<["-"], "fno-modules-global-index">, HelpText<"Do not automatically generate or update the global module index">; def fno_modules_error_recovery : Flag<["-"], "fno-modules-error-recovery">, HelpText<"Do not automatically import modules for error recovery">; -def fmodule_implementation_of : Separate<["-"], "fmodule-implementation-of">, - MetaVarName<"">, - HelpText<"Specify the name of the module whose implementation file this is">; def fmodule_map_file_home_is_cwd : Flag<["-"], "fmodule-map-file-home-is-cwd">, HelpText<"Use the current working directory as the home directory of " "module maps specified by -fmodule-map-file=">; diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index 7abe538275d..08b2d0111d5 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -792,9 +792,10 @@ def fimplicit_module_maps : Flag <["-"], "fimplicit-module-maps">, Group, HelpText<"Implicitly search the file system for module map files.">; def fmodule_maps : Flag <["-"], "fmodule-maps">, Alias; -def fmodule_name : JoinedOrSeparate<["-"], "fmodule-name=">, Group, +def fmodule_name_EQ : Joined<["-"], "fmodule-name=">, Group, Flags<[DriverOption,CC1Option]>, MetaVarName<"">, HelpText<"Specify the name of the module to build">; +def fmodule_name : Separate<["-"], "fmodule-name">, Alias; def fmodule_map_file : Joined<["-"], "fmodule-map-file=">, Group, Flags<[DriverOption,CC1Option]>, MetaVarName<"">, HelpText<"Load this module map file">; diff --git a/include/clang/Lex/ModuleMap.h b/include/clang/Lex/ModuleMap.h index 4d96644beae..97e89f095d7 100644 --- a/include/clang/Lex/ModuleMap.h +++ b/include/clang/Lex/ModuleMap.h @@ -70,15 +70,10 @@ class ModuleMap { /// These are always simple C language options. LangOptions MMapLangOpts; - // The module that we are building; related to \c LangOptions::CurrentModule. - Module *CompilingModule; - -public: - // The module that the .cc source file is associated with. + // The module that the main source file is associated with (the module + // named LangOpts::CurrentModule, if we've loaded it). Module *SourceModule; - std::string SourceModuleName; -private: /// \brief The unshadowed top-level modules that are known. llvm::StringMap Modules; diff --git a/lib/Basic/LangOptions.cpp b/lib/Basic/LangOptions.cpp index 1b08b068604..8c0ecd46ad5 100644 --- a/lib/Basic/LangOptions.cpp +++ b/lib/Basic/LangOptions.cpp @@ -34,7 +34,6 @@ void LangOptions::resetNonModularOptions() { SanitizerBlacklistFiles.clear(); CurrentModule.clear(); - ImplementationOfModule.clear(); } bool LangOptions::isNoBuiltinFunc(const char *Name) const { diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index a3eeb9563f8..e644742188c 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -4932,7 +4932,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, // -fmodule-name specifies the module that is currently being built (or // used for header checking by -fmodule-maps). - Args.AddLastArg(CmdArgs, options::OPT_fmodule_name); + Args.AddLastArg(CmdArgs, options::OPT_fmodule_name_EQ); // -fmodule-map-file can be used to specify files containing module // definitions. diff --git a/lib/Frontend/ASTUnit.cpp b/lib/Frontend/ASTUnit.cpp index 54ac6048bc1..0080fab13e2 100644 --- a/lib/Frontend/ASTUnit.cpp +++ b/lib/Frontend/ASTUnit.cpp @@ -2814,7 +2814,7 @@ const FileEntry *ASTUnit::getPCHFile() { } bool ASTUnit::isModuleFile() { - return isMainFileAST() && !ASTFileLangOpts.CurrentModule.empty(); + return isMainFileAST() && ASTFileLangOpts.CompilingModule; } void ASTUnit::PreambleData::countLines() const { diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp index cdb9cf20a0b..ae34582df05 100644 --- a/lib/Frontend/CompilerInstance.cpp +++ b/lib/Frontend/CompilerInstance.cpp @@ -1405,8 +1405,7 @@ CompilerInstance::loadModule(SourceLocation ImportLoc, // when both the preprocessor and parser see the same import declaration. if (ImportLoc.isValid() && LastModuleImportLoc == ImportLoc) { // Make the named module visible. - if (LastModuleImportResult && ModuleName != getLangOpts().CurrentModule && - ModuleName != getLangOpts().ImplementationOfModule) + if (LastModuleImportResult && ModuleName != getLangOpts().CurrentModule) ModuleManager->makeModuleVisible(LastModuleImportResult, Visibility, ImportLoc); return LastModuleImportResult; @@ -1420,8 +1419,7 @@ CompilerInstance::loadModule(SourceLocation ImportLoc, if (Known != KnownModules.end()) { // Retrieve the cached top-level module. Module = Known->second; - } else if (ModuleName == getLangOpts().CurrentModule || - ModuleName == getLangOpts().ImplementationOfModule) { + } else if (ModuleName == getLangOpts().CurrentModule) { // This is the module we're building. Module = PP->getHeaderSearchInfo().lookupModule(ModuleName); Known = KnownModules.insert(std::make_pair(Path[0].first, Module)).first; @@ -1599,10 +1597,6 @@ CompilerInstance::loadModule(SourceLocation ImportLoc, } } - // Don't make the module visible if we are in the implementation. - if (ModuleName == getLangOpts().ImplementationOfModule) - return ModuleLoadResult(Module, false); - // Make the named module visible, if it's not already part of the module // we are parsing. if (ModuleName != getLangOpts().CurrentModule) { diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 013ab461325..a8329a4fbed 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1747,10 +1747,8 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, Opts.DebuggerCastResultToId = Args.hasArg(OPT_fdebugger_cast_result_to_id); Opts.DebuggerObjCLiteral = Args.hasArg(OPT_fdebugger_objc_literal); Opts.ApplePragmaPack = Args.hasArg(OPT_fapple_pragma_pack); - Opts.CurrentModule = Args.getLastArgValue(OPT_fmodule_name); + Opts.CurrentModule = Args.getLastArgValue(OPT_fmodule_name_EQ); Opts.AppExt = Args.hasArg(OPT_fapplication_extension); - Opts.ImplementationOfModule = - Args.getLastArgValue(OPT_fmodule_implementation_of); Opts.ModuleFeatures = Args.getAllArgValues(OPT_fmodule_feature); std::sort(Opts.ModuleFeatures.begin(), Opts.ModuleFeatures.end()); Opts.NativeHalfType |= Args.hasArg(OPT_fnative_half_type); @@ -1769,12 +1767,6 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, Args.hasFlag(OPT_fdeclspec, OPT_fno_declspec, (Opts.MicrosoftExt || Opts.Borland || Opts.CUDA)); - if (!Opts.CurrentModule.empty() && !Opts.ImplementationOfModule.empty() && - Opts.CurrentModule != Opts.ImplementationOfModule) { - Diags.Report(diag::err_conflicting_module_names) - << Opts.CurrentModule << Opts.ImplementationOfModule; - } - // For now, we only support local submodule visibility in C++ (because we // heavily depend on the ODR for merging redefinitions). if (Opts.ModulesLocalVisibility && !Opts.CPlusPlus) diff --git a/lib/Frontend/FrontendActions.cpp b/lib/Frontend/FrontendActions.cpp index 0a70bbbd20d..9c4c6cec255 100644 --- a/lib/Frontend/FrontendActions.cpp +++ b/lib/Frontend/FrontendActions.cpp @@ -270,6 +270,8 @@ collectModuleHeaderIncludes(const LangOptions &LangOpts, FileManager &FileMgr, bool GenerateModuleAction::BeginSourceFileAction(CompilerInstance &CI, StringRef Filename) { + CI.getLangOpts().CompilingModule = true; + // Find the module map file. const FileEntry *ModuleMap = CI.getFileManager().getFile(Filename, /*openFile*/true); diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp index 7cdda38682e..2efdb7402ee 100644 --- a/lib/Lex/ModuleMap.cpp +++ b/lib/Lex/ModuleMap.cpp @@ -89,7 +89,7 @@ ModuleMap::ModuleMap(SourceManager &SourceMgr, DiagnosticsEngine &Diags, HeaderSearch &HeaderInfo) : SourceMgr(SourceMgr), Diags(Diags), LangOpts(LangOpts), Target(Target), HeaderInfo(HeaderInfo), BuiltinIncludeDir(nullptr), - CompilingModule(nullptr), SourceModule(nullptr), NumCreatedModules(0) { + SourceModule(nullptr), NumCreatedModules(0) { MMapLangOpts.LineComment = true; } @@ -344,8 +344,8 @@ ModuleMap::KnownHeader ModuleMap::findModuleForHeader(const FileEntry *File) { ModuleMap::KnownHeader Result; // Iterate over all modules that 'File' is part of to find the best fit. for (KnownHeader &H : Known->second) { - // Prefer a header from the current module over all others. - if (H.getModule()->getTopLevelModule() == CompilingModule) + // Prefer a header from the source module over all others. + if (H.getModule()->getTopLevelModule() == SourceModule) return MakeResult(H); if (!Result || isBetterKnownHeader(H, Result)) Result = H; @@ -557,17 +557,11 @@ ModuleMap::findOrCreateModule(StringRef Name, Module *Parent, bool IsFramework, // Create a new module with this name. Module *Result = new Module(Name, SourceLocation(), Parent, IsFramework, IsExplicit, NumCreatedModules++); - if (LangOpts.CurrentModule == Name) { - SourceModule = Result; - SourceModuleName = Name; - } if (!Parent) { + if (LangOpts.CurrentModule == Name) + SourceModule = Result; Modules[Name] = Result; ModuleScopeIDs[Result] = CurrentModuleScopeID; - if (!LangOpts.CurrentModule.empty() && !CompilingModule && - Name == LangOpts.CurrentModule) { - CompilingModule = Result; - } } return std::make_pair(Result, true); } @@ -711,9 +705,10 @@ Module *ModuleMap::inferFrameworkModule(const DirectoryEntry *FrameworkDir, ModuleScopeIDs[Result] = CurrentModuleScopeID; InferredModuleAllowedBy[Result] = ModuleMapFile; Result->IsInferred = true; - if (LangOpts.CurrentModule == ModuleName) { - SourceModule = Result; - SourceModuleName = ModuleName; + if (!Parent) { + if (LangOpts.CurrentModule == ModuleName) + SourceModule = Result; + Modules[ModuleName] = Result; } Result->IsSystem |= Attrs.IsSystem; @@ -721,9 +716,6 @@ Module *ModuleMap::inferFrameworkModule(const DirectoryEntry *FrameworkDir, Result->ConfigMacrosExhaustive |= Attrs.IsExhaustive; Result->Directory = FrameworkDir; - if (!Parent) - Modules[ModuleName] = Result; - // umbrella header "umbrella-header-name" // // The "Headers/" component of the name is implied because this is @@ -830,7 +822,8 @@ void ModuleMap::addHeader(Module *Mod, Module::Header Header, HeaderList.push_back(KH); Mod->Headers[headerRoleToKind(Role)].push_back(std::move(Header)); - bool isCompilingModuleHeader = Mod->getTopLevelModule() == CompilingModule; + bool isCompilingModuleHeader = + LangOpts.CompilingModule && Mod->getTopLevelModule() == SourceModule; if (!Imported || isCompilingModuleHeader) { // When we import HeaderFileInfo, the external source is expected to // set the isModuleHeader flag itself. diff --git a/lib/Lex/PPDirectives.cpp b/lib/Lex/PPDirectives.cpp index e5cf43e1717..6bdf98e3d90 100644 --- a/lib/Lex/PPDirectives.cpp +++ b/lib/Lex/PPDirectives.cpp @@ -576,23 +576,23 @@ void Preprocessor::PTHSkipExcludedConditionalBlock() { } Module *Preprocessor::getModuleForLocation(SourceLocation Loc) { - ModuleMap &ModMap = HeaderInfo.getModuleMap(); - if (SourceMgr.isInMainFile(Loc)) { - if (Module *CurMod = getCurrentModule()) - return CurMod; // Compiling a module. - return HeaderInfo.getModuleMap().SourceModule; // Compiling a source. - } - // Try to determine the module of the include directive. - // FIXME: Look into directly passing the FileEntry from LookupFile instead. - FileID IDOfIncl = SourceMgr.getFileID(SourceMgr.getExpansionLoc(Loc)); - if (const FileEntry *EntryOfIncl = SourceMgr.getFileEntryForID(IDOfIncl)) { - // The include comes from a file. - return ModMap.findModuleForHeader(EntryOfIncl).getModule(); - } else { - // The include does not come from a file, - // so it is probably a module compilation. - return getCurrentModule(); + if (!SourceMgr.isInMainFile(Loc)) { + // Try to determine the module of the include directive. + // FIXME: Look into directly passing the FileEntry from LookupFile instead. + FileID IDOfIncl = SourceMgr.getFileID(SourceMgr.getExpansionLoc(Loc)); + if (const FileEntry *EntryOfIncl = SourceMgr.getFileEntryForID(IDOfIncl)) { + // The include comes from an included file. + return HeaderInfo.getModuleMap() + .findModuleForHeader(EntryOfIncl) + .getModule(); + } } + + // This is either in the main file or not in a file at all. It belongs + // to the current module, if there is one. + return getLangOpts().CurrentModule.empty() + ? nullptr + : HeaderInfo.lookupModule(getLangOpts().CurrentModule); } Module *Preprocessor::getModuleContainingLocation(SourceLocation Loc) { @@ -1672,10 +1672,7 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc, // are processing this module textually (because we're building the module). if (File && SuggestedModule && getLangOpts().Modules && SuggestedModule.getModule()->getTopLevelModuleName() != - getLangOpts().CurrentModule && - SuggestedModule.getModule()->getTopLevelModuleName() != - getLangOpts().ImplementationOfModule) { - + getLangOpts().CurrentModule) { // If this include corresponds to a module but that module is // unavailable, diagnose the situation and bail out. if (!SuggestedModule.getModule()->isAvailable()) { diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp index f1e230cb683..885d0ee9f35 100644 --- a/lib/Lex/PPMacroExpansion.cpp +++ b/lib/Lex/PPMacroExpansion.cpp @@ -1448,8 +1448,9 @@ static bool EvaluateBuildingModule(Token &Tok, return false; } - bool Result - = Tok.getIdentifierInfo()->getName() == PP.getLangOpts().CurrentModule; + bool Result = + PP.getLangOpts().CompilingModule && + Tok.getIdentifierInfo()->getName() == PP.getLangOpts().CurrentModule; // Get ')'. PP.LexNonComment(Tok); diff --git a/lib/Lex/Preprocessor.cpp b/lib/Lex/Preprocessor.cpp index 142d9ce0904..70b992170c9 100644 --- a/lib/Lex/Preprocessor.cpp +++ b/lib/Lex/Preprocessor.cpp @@ -477,7 +477,7 @@ void Preprocessor::CreateString(StringRef Str, Token &Tok, } Module *Preprocessor::getCurrentModule() { - if (getLangOpts().CurrentModule.empty()) + if (!getLangOpts().CompilingModule) return nullptr; return getHeaderSearchInfo().lookupModule(getLangOpts().CurrentModule); diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index e69d0a6d193..c6db4f9d5e6 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -14649,11 +14649,10 @@ DeclResult Sema::ActOnModuleImport(SourceLocation AtLoc, // of the same top-level module. Until we do, make it an error rather than // silently ignoring the import. if (Mod->getTopLevelModuleName() == getLangOpts().CurrentModule) - Diag(ImportLoc, diag::err_module_self_import) + Diag(ImportLoc, getLangOpts().CompilingModule + ? diag::err_module_self_import + : diag::err_module_import_in_implementation) << Mod->getFullModuleName() << getLangOpts().CurrentModule; - else if (Mod->getTopLevelModuleName() == getLangOpts().ImplementationOfModule) - Diag(ImportLoc, diag::err_module_import_in_implementation) - << Mod->getFullModuleName() << getLangOpts().ImplementationOfModule; SmallVector IdentifierLocs; Module *ModCheck = Mod; @@ -14687,11 +14686,13 @@ void Sema::ActOnModuleInclude(SourceLocation DirectiveLoc, Module *Mod) { TUKind == TU_Module && getSourceManager().isWrittenInMainFile(DirectiveLoc); - // Similarly, if this module is specified by -fmodule-implementation-of - // don't actually synthesize an illegal module import. - bool ShouldAddImport = !IsInModuleIncludes && - (getLangOpts().ImplementationOfModule.empty() || - getLangOpts().ImplementationOfModule != Mod->getTopLevelModuleName()); + // Similarly, if we're in the implementation of a module, don't + // synthesize an illegal module import. FIXME: Why not? + bool ShouldAddImport = + !IsInModuleIncludes && + (getLangOpts().CompilingModule || + getLangOpts().CurrentModule.empty() || + getLangOpts().CurrentModule != Mod->getTopLevelModuleName()); // If this module import was due to an inclusion directive, create an // implicit import declaration to capture it in the AST. diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index c056284f545..60410bc62b8 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -3194,7 +3194,7 @@ void Sema::addMethodToGlobalList(ObjCMethodList *List, ObjCMethodList *Previous = List; for (; List; Previous = List, List = List->getNext()) { // If we are building a module, keep all of the methods. - if (getLangOpts().Modules && !getLangOpts().CurrentModule.empty()) + if (getLangOpts().CompilingModule) continue; if (!MatchTwoMethodDeclarations(Method, List->getMethod())) { diff --git a/test/Modules/Inputs/explicit-build/a.h b/test/Modules/Inputs/explicit-build/a.h index 5e3602f58ff..a52f7357ff7 100644 --- a/test/Modules/Inputs/explicit-build/a.h +++ b/test/Modules/Inputs/explicit-build/a.h @@ -1,4 +1,4 @@ -#if !__building_module(a) +#if !__building_module(a) && !BUILDING_A_PCH #error "should only get here when building module a" #endif diff --git a/test/Modules/explicit-build.cpp b/test/Modules/explicit-build.cpp index 2a5b70dce6c..a6f6a6268c1 100644 --- a/test/Modules/explicit-build.cpp +++ b/test/Modules/explicit-build.cpp @@ -143,7 +143,7 @@ // ------------------------------- // Try to import a PCH with -fmodule-file= // RUN: %clang_cc1 -x c++ -std=c++11 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -Rmodule-build -fno-modules-error-recovery \ -// RUN: -fmodule-name=a -emit-pch %S/Inputs/explicit-build/a.h -o %t/a.pch \ +// RUN: -fmodule-name=a -emit-pch %S/Inputs/explicit-build/a.h -o %t/a.pch -DBUILDING_A_PCH \ // RUN: 2>&1 | FileCheck --check-prefix=CHECK-NO-IMPLICIT-BUILD %s --allow-empty // // RUN: not %clang_cc1 -x c++ -std=c++11 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -Rmodule-build -fno-modules-error-recovery \ diff --git a/test/Modules/implementation-of-module.m b/test/Modules/implementation-of-module.m index 37e2cfbe30f..c07c52c3fc6 100644 --- a/test/Modules/implementation-of-module.m +++ b/test/Modules/implementation-of-module.m @@ -1,22 +1,18 @@ -// RUN: not %clang_cc1 -fmodule-implementation-of Foo -fmodule-name=Bar %s 2>&1 \ -// RUN: | FileCheck -check-prefix=CHECK-IMPL-OF-ERR %s -// CHECK-IMPL-OF-ERR: conflicting module names specified: '-fmodule-name=Bar' and '-fmodule-implementation-of Foo' - // RUN: rm -rf %t // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -w -Werror=auto-import %s -I %S/Inputs \ -// RUN: -fmodule-implementation-of category_right -fsyntax-only +// RUN: -fmodule-name=category_right -fsyntax-only // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -w -Werror=auto-import %s -I %S/Inputs \ -// RUN: -fmodule-implementation-of category_right -dM -E -o - 2>&1 | FileCheck %s +// RUN: -fmodule-name=category_right -dM -E -o - 2>&1 | FileCheck %s // CHECK-NOT: __building_module // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -w -Werror=auto-import %s -I %S/Inputs \ -// RUN: -fmodule-implementation-of category_left -verify +// RUN: -fmodule-name=category_left -verify // RUN: %clang_cc1 -x objective-c-header -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -w -Werror=auto-import %s -I %S/Inputs \ -// RUN: -fmodule-implementation-of category_right -emit-pch -o %t.pch +// RUN: -fmodule-name=category_right -emit-pch -o %t.pch // RUN: %clang_cc1 -x objective-c-header -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -w -Werror=auto-import %s -I %S/Inputs \ -// RUN: -DWITH_PREFIX -fmodules-ignore-macro=WITH_PREFIX -include-pch %t.pch -fmodule-implementation-of category_right +// RUN: -DWITH_PREFIX -fmodules-ignore-macro=WITH_PREFIX -include-pch %t.pch -fmodule-name=category_right #ifndef WITH_PREFIX diff --git a/test/Modules/import-self.m b/test/Modules/import-self.m index aa743713237..e5980159245 100644 --- a/test/Modules/import-self.m +++ b/test/Modules/import-self.m @@ -6,6 +6,6 @@ // RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t \ // RUN: -I %S/Inputs/submodules -fmodule-name=import_self %s \ // RUN: 2>&1 | FileCheck -check-prefix=CHECK-fmodule-name %s -// CHECK-fmodule-name: import of module 'import_self.b' appears within same top-level module 'import_self' +// CHECK-fmodule-name: @import of module 'import_self.b' in implementation of 'import_self' @import import_self.b; From 5c908dd4de200935214eef667a8fb533e68fd7f9 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 14 Mar 2016 17:52:37 +0000 Subject: [PATCH 443/742] [modules] Don't diagnose non-modular includes from modular files that are implementation units of modules rather than interface units. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263449 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Lex/ModuleMap.h | 6 ++++++ lib/Lex/ModuleMap.cpp | 3 ++- lib/Lex/PPDirectives.cpp | 10 +++++++--- test/Modules/Inputs/non-module.h | 4 ++++ test/Modules/include-own-headers.m | 4 ++++ 5 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 test/Modules/Inputs/non-module.h create mode 100644 test/Modules/include-own-headers.m diff --git a/include/clang/Lex/ModuleMap.h b/include/clang/Lex/ModuleMap.h index 97e89f095d7..dfca6ed7881 100644 --- a/include/clang/Lex/ModuleMap.h +++ b/include/clang/Lex/ModuleMap.h @@ -325,12 +325,18 @@ class ModuleMap { /// /// \param RequestingModule The module including a file. /// + /// \param RequestingModuleIsModuleInterface \c true if the inclusion is in + /// the interface of RequestingModule, \c false if it's in the + /// implementation of RequestingModule. Value is ignored and + /// meaningless if RequestingModule is nullptr. + /// /// \param FilenameLoc The location of the inclusion's filename. /// /// \param Filename The included filename as written. /// /// \param File The included file. void diagnoseHeaderInclusion(Module *RequestingModule, + bool RequestingModuleIsModuleInterface, SourceLocation FilenameLoc, StringRef Filename, const FileEntry *File); diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp index 2efdb7402ee..0ee8143c3b7 100644 --- a/lib/Lex/ModuleMap.cpp +++ b/lib/Lex/ModuleMap.cpp @@ -242,6 +242,7 @@ static Module *getTopLevelOrNull(Module *M) { } void ModuleMap::diagnoseHeaderInclusion(Module *RequestingModule, + bool RequestingModuleIsModuleInterface, SourceLocation FilenameLoc, StringRef Filename, const FileEntry *File) { @@ -304,7 +305,7 @@ void ModuleMap::diagnoseHeaderInclusion(Module *RequestingModule, if (LangOpts.ModulesStrictDeclUse) { Diags.Report(FilenameLoc, diag::err_undeclared_use_of_module) << RequestingModule->getFullModuleName() << Filename; - } else if (RequestingModule) { + } else if (RequestingModule && RequestingModuleIsModuleInterface) { diag::kind DiagID = RequestingModule->getTopLevelModule()->IsFramework ? diag::warn_non_modular_include_in_framework_module : diag::warn_non_modular_include_in_module; diff --git a/lib/Lex/PPDirectives.cpp b/lib/Lex/PPDirectives.cpp index 6bdf98e3d90..3506974cac5 100644 --- a/lib/Lex/PPDirectives.cpp +++ b/lib/Lex/PPDirectives.cpp @@ -612,6 +612,7 @@ const FileEntry *Preprocessor::LookupFile( ModuleMap::KnownHeader *SuggestedModule, bool SkipCache) { Module *RequestingModule = getModuleForLocation(FilenameLoc); + bool RequestingModuleIsModuleInterface = !SourceMgr.isInMainFile(FilenameLoc); // If the header lookup mechanism may be relative to the current inclusion // stack, record the parent #includes. @@ -686,7 +687,8 @@ const FileEntry *Preprocessor::LookupFile( if (FE) { if (SuggestedModule && !LangOpts.AsmPreprocessor) HeaderInfo.getModuleMap().diagnoseHeaderInclusion( - RequestingModule, FilenameLoc, Filename, FE); + RequestingModule, RequestingModuleIsModuleInterface, FilenameLoc, + Filename, FE); return FE; } @@ -702,7 +704,8 @@ const FileEntry *Preprocessor::LookupFile( SuggestedModule))) { if (SuggestedModule && !LangOpts.AsmPreprocessor) HeaderInfo.getModuleMap().diagnoseHeaderInclusion( - RequestingModule, FilenameLoc, Filename, FE); + RequestingModule, RequestingModuleIsModuleInterface, FilenameLoc, + Filename, FE); return FE; } } @@ -717,7 +720,8 @@ const FileEntry *Preprocessor::LookupFile( RequestingModule, SuggestedModule))) { if (SuggestedModule && !LangOpts.AsmPreprocessor) HeaderInfo.getModuleMap().diagnoseHeaderInclusion( - RequestingModule, FilenameLoc, Filename, FE); + RequestingModule, RequestingModuleIsModuleInterface, + FilenameLoc, Filename, FE); return FE; } } diff --git a/test/Modules/Inputs/non-module.h b/test/Modules/Inputs/non-module.h new file mode 100644 index 00000000000..c295900bd10 --- /dev/null +++ b/test/Modules/Inputs/non-module.h @@ -0,0 +1,4 @@ +#ifndef NON_MODULE_H +#define NON_MODULE_H + +#endif diff --git a/test/Modules/include-own-headers.m b/test/Modules/include-own-headers.m new file mode 100644 index 00000000000..a5a85312ec3 --- /dev/null +++ b/test/Modules/include-own-headers.m @@ -0,0 +1,4 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -fmodules -fmodule-name=Module -fimplicit-module-maps -fmodules-cache-path=%t -Werror=non-modular-include-in-framework-module -F%S/Inputs -I%S -fsyntax-only %s +#include "Module/Module.h" +#include "Inputs/non-module.h" From d77a746889473e59362b03c3e4dc221d3716c897 Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Wed, 6 Apr 2016 23:28:26 +0000 Subject: [PATCH 444/742] Keep -fmodule-implementation-of as an alias of -fmodule-name. This helps us transitioning to -fmodule-name. Once the transitioning is done, we can remove this alias. rdar://24800983 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265616 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Driver/Options.td | 2 ++ test/Modules/implementation-of-module.m | 10 +++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index 08b2d0111d5..a0cc8cd7bf1 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -796,6 +796,8 @@ def fmodule_name_EQ : Joined<["-"], "fmodule-name=">, Group, Flags<[DriverOption,CC1Option]>, MetaVarName<"">, HelpText<"Specify the name of the module to build">; def fmodule_name : Separate<["-"], "fmodule-name">, Alias; +def fmodule_implementation_of : Separate<["-"], "fmodule-implementation-of">, + Flags<[CC1Option]>, Alias; def fmodule_map_file : Joined<["-"], "fmodule-map-file=">, Group, Flags<[DriverOption,CC1Option]>, MetaVarName<"">, HelpText<"Load this module map file">; diff --git a/test/Modules/implementation-of-module.m b/test/Modules/implementation-of-module.m index c07c52c3fc6..712f12c5654 100644 --- a/test/Modules/implementation-of-module.m +++ b/test/Modules/implementation-of-module.m @@ -1,18 +1,18 @@ // RUN: rm -rf %t // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -w -Werror=auto-import %s -I %S/Inputs \ -// RUN: -fmodule-name=category_right -fsyntax-only +// RUN: -fmodule-implementation-of category_right -fsyntax-only // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -w -Werror=auto-import %s -I %S/Inputs \ -// RUN: -fmodule-name=category_right -dM -E -o - 2>&1 | FileCheck %s +// RUN: -fmodule-implementation-of category_right -dM -E -o - 2>&1 | FileCheck %s // CHECK-NOT: __building_module // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -w -Werror=auto-import %s -I %S/Inputs \ -// RUN: -fmodule-name=category_left -verify +// RUN: -fmodule-implementation-of category_left -verify // RUN: %clang_cc1 -x objective-c-header -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -w -Werror=auto-import %s -I %S/Inputs \ -// RUN: -fmodule-name=category_right -emit-pch -o %t.pch +// RUN: -fmodule-implementation-of category_right -emit-pch -o %t.pch // RUN: %clang_cc1 -x objective-c-header -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -w -Werror=auto-import %s -I %S/Inputs \ -// RUN: -DWITH_PREFIX -fmodules-ignore-macro=WITH_PREFIX -include-pch %t.pch -fmodule-name=category_right +// RUN: -DWITH_PREFIX -fmodules-ignore-macro=WITH_PREFIX -include-pch %t.pch -fmodule-implementation-of category_right #ifndef WITH_PREFIX From 3987718dae02455fef23833714b63bfb653b83b9 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 7 Apr 2016 10:38:40 -0700 Subject: [PATCH 445/742] [API notes] Process API notes for enumerators. --- lib/Sema/SemaDecl.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index f8de19b9681..5b0f93e67f8 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -14159,6 +14159,8 @@ Decl *Sema::ActOnEnumConstant(Scope *S, Decl *theEnumDecl, Decl *lastEnumConst, // Process attributes. if (Attr) ProcessDeclAttributeList(S, New, Attr); + ProcessAPINotes(New); + // Register this decl in the current scope stack. New->setAccess(TheEnumDecl->getAccess()); PushOnScopeChains(New, S); From 6f4d362b6a35ff084b3cce2e79d51e0c0286ad7e Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 7 Apr 2016 10:38:40 -0700 Subject: [PATCH 446/742] [API notes] Process API notes for enumerators. --- lib/Sema/SemaDecl.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index c6db4f9d5e6..8fb35fe9c68 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -14131,6 +14131,8 @@ Decl *Sema::ActOnEnumConstant(Scope *S, Decl *theEnumDecl, Decl *lastEnumConst, // Process attributes. if (Attr) ProcessDeclAttributeList(S, New, Attr); + ProcessAPINotes(New); + // Register this decl in the current scope stack. New->setAccess(TheEnumDecl->getAccess()); PushOnScopeChains(New, S); From ba6dae2bc583fa56b11fa7095ab736ac623ac704 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Thu, 31 Mar 2016 23:57:45 +0000 Subject: [PATCH 447/742] Adapt to LLVM API change in r265077. EmissionKind moved from DIBuilder to DICompileUnit. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265078 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit e6cf094aca92155c37127ade001796a2831fd8a3) --- lib/CodeGen/CGDebugInfo.cpp | 6 +++--- test/CodeGenCXX/debug-info-namespace.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index e300ed538ac..348fa7c2f96 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -403,8 +403,8 @@ void CGDebugInfo::CreateCompileUnit() { Producer, LO.Optimize, CGM.getCodeGenOpts().DwarfDebugFlags, RuntimeVers, CGM.getCodeGenOpts().SplitDwarfFile, DebugKind <= codegenoptions::DebugLineTablesOnly - ? llvm::DIBuilder::LineTablesOnly - : llvm::DIBuilder::FullDebug, + ? llvm::DICompileUnit::LineTablesOnly + : llvm::DICompileUnit::FullDebug, 0 /* DWOid */, DebugKind != codegenoptions::LocTrackingOnly); } @@ -1746,7 +1746,7 @@ CGDebugInfo::getOrCreateModuleRef(ExternalASTSource::ASTSourceDescriptor Mod, DIB.createCompileUnit(TheCU->getSourceLanguage(), Mod.getModuleName(), Mod.getPath(), TheCU->getProducer(), true, StringRef(), 0, Mod.getASTFile(), - llvm::DIBuilder::FullDebug, Signature); + llvm::DICompileUnit::FullDebug, Signature); DIB.finalize(); } llvm::DIModule *Parent = diff --git a/test/CodeGenCXX/debug-info-namespace.cpp b/test/CodeGenCXX/debug-info-namespace.cpp index 4933ae96745..bebb24ab5d6 100644 --- a/test/CodeGenCXX/debug-info-namespace.cpp +++ b/test/CodeGenCXX/debug-info-namespace.cpp @@ -103,7 +103,7 @@ void B::func_fwd() {} // CHECK: [[M17]] = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: [[CTXT]], entity: [[I]] // CHECK-GMLT: [[CU:![0-9]+]] = distinct !DICompileUnit( -// CHECK-GMLT-SAME: emissionKind: 2, +// CHECK-GMLT-SAME: emissionKind: LineTablesOnly, // CHECK-GMLT-NOT: imports: // CHECK-NOLIMIT: !DICompositeType(tag: DW_TAG_structure_type, name: "bar",{{.*}} line: 6, From 46411ca70cc1925c4ff1e6a6db6d4a95baa70fd3 Mon Sep 17 00:00:00 2001 From: Gabor Horvath Date: Wed, 30 Mar 2016 10:08:59 +0000 Subject: [PATCH 448/742] [analyzer] Fix an assertion fail in hash generation. In case the (uniqueing) location of the diagnostic is in a line that only contains whitespaces there was an assertion fail during issue hash generation. Unfortunately I am unable to reproduce this error with the built in checkers, so no there is no failing test case with this patch. It would be possible to write a debug checker for that purpuse but it does not worth the effort. Differential Revision: http://reviews.llvm.org/D18210 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@264851 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit a26e4e76c2abe29a074fac69ae56575a5d050333) --- lib/StaticAnalyzer/Core/IssueHash.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/StaticAnalyzer/Core/IssueHash.cpp b/lib/StaticAnalyzer/Core/IssueHash.cpp index c352f12111f..bd5c81179ad 100644 --- a/lib/StaticAnalyzer/Core/IssueHash.cpp +++ b/lib/StaticAnalyzer/Core/IssueHash.cpp @@ -132,8 +132,11 @@ static std::string NormalizeLine(const SourceManager &SM, FullSourceLoc &L, StringRef Str = GetNthLineOfFile(SM.getBuffer(L.getFileID(), L), L.getExpansionLineNumber()); - unsigned col = Str.find_first_not_of(Whitespaces); - col++; + StringRef::size_type col = Str.find_first_not_of(Whitespaces); + if (col == StringRef::npos) + col = 1; // The line only contains whitespace. + else + col++; SourceLocation StartOfLine = SM.translateLineCol(SM.getFileID(L), L.getExpansionLineNumber(), col); llvm::MemoryBuffer *Buffer = From 2f2620f816c29f47497b0701973a98857ee6741c Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Fri, 8 Apr 2016 19:59:16 +0000 Subject: [PATCH 449/742] [analyzer] Teach trackNullOrUndefValue about calls to property accessors. Teach trackNullOrUndefValue() how to look through PseudoObjectExprs to find the underlying method call for property getters. This makes over-suppression of 'return nil' in getters consistent with the similar over-suppression for method and function calls. rdar://problem/24437252 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265839 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 9535640b53d0c29ef6ae54db653c7bc7b123c7a7) --- .../Core/BugReporterVisitors.cpp | 6 ++++ .../inlining/false-positive-suppression.m | 36 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 859a2220e09..e0f014714f7 100644 --- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -908,6 +908,12 @@ static const Expr *peelOffOuterExpr(const Expr *Ex, return peelOffOuterExpr(EWC->getSubExpr(), N); if (const OpaqueValueExpr *OVE = dyn_cast(Ex)) return peelOffOuterExpr(OVE->getSourceExpr(), N); + if (auto *POE = dyn_cast(Ex)) { + auto *PropRef = dyn_cast(POE->getSyntacticForm()); + if (PropRef && PropRef->isMessagingGetter()) { + return peelOffOuterExpr(POE->getSemanticExpr(1), N); + } + } // Peel off the ternary operator. if (const ConditionalOperator *CO = dyn_cast(Ex)) { diff --git a/test/Analysis/inlining/false-positive-suppression.m b/test/Analysis/inlining/false-positive-suppression.m index 53ec138367e..7be1cb8472f 100644 --- a/test/Analysis/inlining/false-positive-suppression.m +++ b/test/Analysis/inlining/false-positive-suppression.m @@ -36,3 +36,39 @@ void testNilReceiver(int coin) { else testNilReceiverHelperB([[x getObject] getPtr]); } + +// FALSE NEGATIVES (over-suppression) + +__attribute__((objc_root_class)) +@interface SomeClass +-(int *)methodReturningNull; + +@property(readonly) int *propertyReturningNull; + +@end + +@implementation SomeClass +-(int *)methodReturningNull { + return 0; +} + +-(int *)propertyReturningNull { + return 0; +} +@end + +void testMethodReturningNull(SomeClass *sc) { + int *result = [sc methodReturningNull]; + *result = 1; +#ifndef SUPPRESSED + // expected-warning@-2 {{Dereference of null pointer}} +#endif +} + +void testPropertyReturningNull(SomeClass *sc) { + int *result = sc.propertyReturningNull; + *result = 1; +#ifndef SUPPRESSED + // expected-warning@-2 {{Dereference of null pointer}} +#endif +} From f0b7fa6a9b310057935c413475815fc54595f9a5 Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Thu, 7 Apr 2016 19:30:20 +0000 Subject: [PATCH 450/742] NFC: simplify code in BuildInstanceMessage. Instead of searching the global pool multiple times: in LookupFactoryMethodInGlobalPool, LookupInstanceMethodInGlobalPool, CollectMultipleMethodsInGlobalPool, and AreMultipleMethodsInGlobalPool, we now collect the method candidates in CollectMultipleMethodsInGlobalPool only, and other functions will use the collected method set. This commit adds parameter "Methods" to AreMultipleMethodsInGlobalPool, and SelectBestMethod. It also changes the implementation of CollectMultipleMethodsInGlobalPool to collect the desired kind first, if none is found, to collect the other kind. This avoids the need to call both LookupFactoryMethodInGlobalPool and LookupInstanceMethodInGlobalPool. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265711 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Sema/Sema.h | 30 +++++++------ lib/Sema/SemaDeclObjC.cpp | 53 ++++++++++++++++------- lib/Sema/SemaExprObjC.cpp | 88 +++++++++++++++++++++++---------------- lib/Sema/SemaOverload.cpp | 10 ++--- 4 files changed, 112 insertions(+), 69 deletions(-) diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index c4025381c08..6261ad4cc98 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -3157,25 +3157,31 @@ class Sema { public: /// \brief - Returns instance or factory methods in global method pool for - /// given selector. If no such method or only one method found, function returns - /// false; otherwise, it returns true - bool CollectMultipleMethodsInGlobalPool(Selector Sel, - SmallVectorImpl& Methods, - bool instance); + /// given selector. It checks the desired kind first, if none is found, and + /// parameter checkTheOther is set, it then checks the other kind. If no such + /// method or only one method is found, function returns false; otherwise, it + /// returns true. + bool + CollectMultipleMethodsInGlobalPool(Selector Sel, + SmallVectorImpl& Methods, + bool InstanceFirst, bool CheckTheOther); - bool AreMultipleMethodsInGlobalPool(Selector Sel, ObjCMethodDecl *BestMethod, - SourceRange R, - bool receiverIdOrClass); + bool + AreMultipleMethodsInGlobalPool(Selector Sel, ObjCMethodDecl *BestMethod, + SourceRange R, bool receiverIdOrClass, + SmallVectorImpl& Methods); - void DiagnoseMultipleMethodInGlobalPool(SmallVectorImpl &Methods, - Selector Sel, SourceRange R, - bool receiverIdOrClass); + void + DiagnoseMultipleMethodInGlobalPool(SmallVectorImpl &Methods, + Selector Sel, SourceRange R, + bool receiverIdOrClass); private: /// \brief - Returns a selector which best matches given argument list or /// nullptr if none could be found ObjCMethodDecl *SelectBestMethod(Selector Sel, MultiExprArg Args, - bool IsInstance); + bool IsInstance, + SmallVectorImpl& Methods); /// \brief Record the typo correction failure and return an empty correction. diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index 60410bc62b8..e68f703a6b1 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -3287,25 +3287,57 @@ static bool isAcceptableMethodMismatch(ObjCMethodDecl *chosen, return (chosen->getReturnType()->isIntegerType()); } +/// We first select the type of the method: Instance or Factory, then collect +/// all methods with that type. bool Sema::CollectMultipleMethodsInGlobalPool( - Selector Sel, SmallVectorImpl &Methods, bool instance) { + Selector Sel, SmallVectorImpl &Methods, + bool InstanceFirst, bool CheckTheOther) { if (ExternalSource) ReadMethodPool(Sel); GlobalMethodPool::iterator Pos = MethodPool.find(Sel); if (Pos == MethodPool.end()) return false; + // Gather the non-hidden methods. - ObjCMethodList &MethList = instance ? Pos->second.first : Pos->second.second; + ObjCMethodList &MethList = InstanceFirst ? Pos->second.first : + Pos->second.second; for (ObjCMethodList *M = &MethList; M; M = M->getNext()) if (M->getMethod() && !M->getMethod()->isHidden()) Methods.push_back(M->getMethod()); + + // Return if we find any method with the desired kind. + if (!Methods.empty()) + return Methods.size() > 1; + + if (!CheckTheOther) + return false; + + // Gather the other kind. + ObjCMethodList &MethList2 = InstanceFirst ? Pos->second.second : + Pos->second.first; + for (ObjCMethodList *M = &MethList2; M; M = M->getNext()) + if (M->getMethod() && !M->getMethod()->isHidden()) + Methods.push_back(M->getMethod()); + return Methods.size() > 1; } -bool Sema::AreMultipleMethodsInGlobalPool(Selector Sel, ObjCMethodDecl *BestMethod, - SourceRange R, - bool receiverIdOrClass) { +bool Sema::AreMultipleMethodsInGlobalPool( + Selector Sel, ObjCMethodDecl *BestMethod, SourceRange R, + bool receiverIdOrClass, SmallVectorImpl &Methods) { + // Diagnose finding more than one method in global pool. + SmallVector FilteredMethods; + FilteredMethods.push_back(BestMethod); + + for (auto *M : Methods) + if (M != BestMethod && !M->hasAttr()) + FilteredMethods.push_back(M); + + if (FilteredMethods.size() > 1) + DiagnoseMultipleMethodInGlobalPool(FilteredMethods, Sel, R, + receiverIdOrClass); + GlobalMethodPool::iterator Pos = MethodPool.find(Sel); // Test for no method in the pool which should not trigger any warning by // caller. @@ -3313,17 +3345,6 @@ bool Sema::AreMultipleMethodsInGlobalPool(Selector Sel, ObjCMethodDecl *BestMeth return true; ObjCMethodList &MethList = BestMethod->isInstanceMethod() ? Pos->second.first : Pos->second.second; - - // Diagnose finding more than one method in global pool - SmallVector Methods; - Methods.push_back(BestMethod); - for (ObjCMethodList *ML = &MethList; ML; ML = ML->getNext()) - if (ObjCMethodDecl *M = ML->getMethod()) - if (!M->isHidden() && M != BestMethod && !M->hasAttr()) - Methods.push_back(M); - if (Methods.size() > 1) - DiagnoseMultipleMethodInGlobalPool(Methods, Sel, R, receiverIdOrClass); - return MethList.hasMoreThanOneDecl(); } diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp index ab812fc1dfe..18371c37a69 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/lib/Sema/SemaExprObjC.cpp @@ -2631,22 +2631,22 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, typeBound); if (receiverIsIdLike || ReceiverType->isBlockPointerType() || (Receiver && Context.isObjCNSObjectType(Receiver->getType()))) { - Method = LookupInstanceMethodInGlobalPool(Sel, - SourceRange(LBracLoc, RBracLoc), - receiverIsIdLike); - if (!Method) - Method = LookupFactoryMethodInGlobalPool(Sel, - SourceRange(LBracLoc,RBracLoc), - receiverIsIdLike); - if (Method) { + SmallVector Methods; + CollectMultipleMethodsInGlobalPool(Sel, Methods, true/*InstanceFirst*/, + true/*CheckTheOther*/); + if (!Methods.empty()) { + // We chose the first method as the initial condidate, then try to + // select a better one. + Method = Methods[0]; + if (ObjCMethodDecl *BestMethod = - SelectBestMethod(Sel, ArgsIn, Method->isInstanceMethod())) + SelectBestMethod(Sel, ArgsIn, Method->isInstanceMethod(), Methods)) Method = BestMethod; + if (!AreMultipleMethodsInGlobalPool(Sel, Method, SourceRange(LBracLoc, RBracLoc), - receiverIsIdLike)) { - DiagnoseUseOfDecl(Method, SelLoc); - } + receiverIsIdLike, Methods)) + DiagnoseUseOfDecl(Method, SelLoc); } } else if (ReceiverType->isObjCClassOrClassKindOfType() || ReceiverType->isObjCQualifiedClassType()) { @@ -2684,25 +2684,32 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, if (!Method) { // If not messaging 'self', look for any factory method named 'Sel'. if (!Receiver || !isSelfExpr(Receiver)) { - Method = LookupFactoryMethodInGlobalPool(Sel, - SourceRange(LBracLoc, RBracLoc)); - if (!Method) { - // If no class (factory) method was found, check if an _instance_ - // method of the same name exists in the root class only. - Method = LookupInstanceMethodInGlobalPool(Sel, - SourceRange(LBracLoc, RBracLoc)); - if (Method) - if (const ObjCInterfaceDecl *ID = - dyn_cast(Method->getDeclContext())) { - if (ID->getSuperClass()) - Diag(SelLoc, diag::warn_root_inst_method_not_found) - << Sel << SourceRange(LBracLoc, RBracLoc); - } + // If no class (factory) method was found, check if an _instance_ + // method of the same name exists in the root class only. + SmallVector Methods; + CollectMultipleMethodsInGlobalPool(Sel, Methods, + false/*InstanceFirst*/, + true/*CheckTheOther*/); + if (!Methods.empty()) { + // We chose the first method as the initial condidate, then try + // to select a better one. + Method = Methods[0]; + + // If we find an instance method, emit waring. + if (Method->isInstanceMethod()) { + if (const ObjCInterfaceDecl *ID = + dyn_cast(Method->getDeclContext())) { + if (ID->getSuperClass()) + Diag(SelLoc, diag::warn_root_inst_method_not_found) + << Sel << SourceRange(LBracLoc, RBracLoc); + } + } + + if (ObjCMethodDecl *BestMethod = + SelectBestMethod(Sel, ArgsIn, Method->isInstanceMethod(), + Methods)) + Method = BestMethod; } - if (Method) - if (ObjCMethodDecl *BestMethod = - SelectBestMethod(Sel, ArgsIn, Method->isInstanceMethod())) - Method = BestMethod; } } } @@ -2767,15 +2774,24 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, // behavior isn't very desirable, however we need it for GCC // compatibility. FIXME: should we deviate?? if (OCIType->qual_empty()) { - Method = LookupInstanceMethodInGlobalPool(Sel, - SourceRange(LBracLoc, RBracLoc)); - if (Method) { - if (auto BestMethod = - SelectBestMethod(Sel, ArgsIn, Method->isInstanceMethod())) + SmallVector Methods; + CollectMultipleMethodsInGlobalPool(Sel, Methods, + true/*InstanceFirst*/, + false/*CheckTheOther*/); + if (!Methods.empty()) { + // We chose the first method as the initial condidate, then try + // to select a better one. + Method = Methods[0]; + + if (ObjCMethodDecl *BestMethod = + SelectBestMethod(Sel, ArgsIn, Method->isInstanceMethod(), + Methods)) Method = BestMethod; + AreMultipleMethodsInGlobalPool(Sel, Method, SourceRange(LBracLoc, RBracLoc), - true); + true/*receiverIdOrClass*/, + Methods); } if (Method && !forwardClass) Diag(SelLoc, diag::warn_maynot_respond) diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 7685fec3f26..597661bbdea 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -5847,12 +5847,12 @@ Sema::AddOverloadCandidate(FunctionDecl *Function, } } -ObjCMethodDecl *Sema::SelectBestMethod(Selector Sel, MultiExprArg Args, - bool IsInstance) { - SmallVector Methods; - if (!CollectMultipleMethodsInGlobalPool(Sel, Methods, IsInstance)) +ObjCMethodDecl * +Sema::SelectBestMethod(Selector Sel, MultiExprArg Args, bool IsInstance, + SmallVectorImpl &Methods) { + if (Methods.size() <= 1) return nullptr; - + for (unsigned b = 0, e = Methods.size(); b < e; b++) { bool Match = true; ObjCMethodDecl *Method = Methods[b]; From c49c337a2b911bc8002205f5f74ccd04310319da Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Thu, 7 Apr 2016 19:32:24 +0000 Subject: [PATCH 451/742] [ObjC kindof] Use type bound to filter out the candidate methods. rdar://21306753 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265712 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Sema/Sema.h | 3 ++- lib/Sema/SemaDeclObjC.cpp | 48 +++++++++++++++++++++++++++++++++++---- lib/Sema/SemaExprObjC.cpp | 5 ++-- test/SemaObjC/kindof.m | 16 +++++++++++-- 4 files changed, 61 insertions(+), 11 deletions(-) diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 6261ad4cc98..ce14f8bf2b4 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -3164,7 +3164,8 @@ class Sema { bool CollectMultipleMethodsInGlobalPool(Selector Sel, SmallVectorImpl& Methods, - bool InstanceFirst, bool CheckTheOther); + bool InstanceFirst, bool CheckTheOther, + const ObjCObjectType *TypeBound = nullptr); bool AreMultipleMethodsInGlobalPool(Selector Sel, ObjCMethodDecl *BestMethod, diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index e68f703a6b1..1a44d2307b7 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -3287,11 +3287,45 @@ static bool isAcceptableMethodMismatch(ObjCMethodDecl *chosen, return (chosen->getReturnType()->isIntegerType()); } +/// Return true if the given method is wthin the type bound. +static bool FilterMethodsByTypeBound(ObjCMethodDecl *Method, + const ObjCObjectType *TypeBound) { + if (!TypeBound) + return true; + + if (TypeBound->isObjCId()) + // FIXME: should we handle the case of bounding to id differently? + return true; + + auto *BoundInterface = TypeBound->getInterface(); + assert(BoundInterface && "unexpected object type!"); + + // Check if the Method belongs to a protocol. We should allow any method + // defined in any protocol, because any subclass could adopt the protocol. + auto *MethodProtocol = dyn_cast(Method->getDeclContext()); + if (MethodProtocol) { + return true; + } + + // If the Method belongs to a class, check if it belongs to the class + // hierarchy of the class bound. + if (ObjCInterfaceDecl *MethodInterface = Method->getClassInterface()) { + // We allow methods declared within classes that are part of the hierarchy + // of the class bound (superclass of, subclass of, or the same as the class + // bound). + return MethodInterface == BoundInterface || + MethodInterface->isSuperClassOf(BoundInterface) || + BoundInterface->isSuperClassOf(MethodInterface); + } + llvm_unreachable("unknow method context"); +} + /// We first select the type of the method: Instance or Factory, then collect /// all methods with that type. bool Sema::CollectMultipleMethodsInGlobalPool( Selector Sel, SmallVectorImpl &Methods, - bool InstanceFirst, bool CheckTheOther) { + bool InstanceFirst, bool CheckTheOther, + const ObjCObjectType *TypeBound) { if (ExternalSource) ReadMethodPool(Sel); @@ -3303,8 +3337,10 @@ bool Sema::CollectMultipleMethodsInGlobalPool( ObjCMethodList &MethList = InstanceFirst ? Pos->second.first : Pos->second.second; for (ObjCMethodList *M = &MethList; M; M = M->getNext()) - if (M->getMethod() && !M->getMethod()->isHidden()) - Methods.push_back(M->getMethod()); + if (M->getMethod() && !M->getMethod()->isHidden()) { + if (FilterMethodsByTypeBound(M->getMethod(), TypeBound)) + Methods.push_back(M->getMethod()); + } // Return if we find any method with the desired kind. if (!Methods.empty()) @@ -3317,8 +3353,10 @@ bool Sema::CollectMultipleMethodsInGlobalPool( ObjCMethodList &MethList2 = InstanceFirst ? Pos->second.second : Pos->second.first; for (ObjCMethodList *M = &MethList2; M; M = M->getNext()) - if (M->getMethod() && !M->getMethod()->isHidden()) - Methods.push_back(M->getMethod()); + if (M->getMethod() && !M->getMethod()->isHidden()) { + if (FilterMethodsByTypeBound(M->getMethod(), TypeBound)) + Methods.push_back(M->getMethod()); + } return Methods.size() > 1; } diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp index 18371c37a69..b8af158072d 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/lib/Sema/SemaExprObjC.cpp @@ -2624,16 +2624,15 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, if (!Method) { // Handle messages to id and __kindof types (where we use the // global method pool). - // FIXME: The type bound is currently ignored by lookup in the - // global pool. const ObjCObjectType *typeBound = nullptr; bool receiverIsIdLike = ReceiverType->isObjCIdOrObjectKindOfType(Context, typeBound); if (receiverIsIdLike || ReceiverType->isBlockPointerType() || (Receiver && Context.isObjCNSObjectType(Receiver->getType()))) { SmallVector Methods; + // If we have a type bound, further filter the methods. CollectMultipleMethodsInGlobalPool(Sel, Methods, true/*InstanceFirst*/, - true/*CheckTheOther*/); + true/*CheckTheOther*/, typeBound); if (!Methods.empty()) { // We chose the first method as the initial condidate, then try to // select a better one. diff --git a/test/SemaObjC/kindof.m b/test/SemaObjC/kindof.m index f205e68ea12..36c9b8f0a2b 100644 --- a/test/SemaObjC/kindof.m +++ b/test/SemaObjC/kindof.m @@ -24,7 +24,7 @@ @interface NSObject - (NSObject *)retain; @end -@interface NSString : NSObject +@interface NSString : NSObject // expected-note{{receiver is instance of class declared here}} - (NSString *)stringByAppendingString:(NSString *)string; + (instancetype)string; @end @@ -246,7 +246,7 @@ void message_kindof_object(__kindof NSString *kindof_NSString) { [kindof_NSString retain]; // in superclass [kindof_NSString stringByAppendingString:0]; // in class [kindof_NSString appendString:0]; // in subclass - [kindof_NSString numberByAddingNumber: 0]; // FIXME: in unrelated class + [kindof_NSString numberByAddingNumber: 0]; // expected-warning{{instance method '-numberByAddingNumber:' not found (return type defaults to 'id')}} [kindof_NSString randomMethod]; // in protocol } @@ -263,6 +263,18 @@ void message_kindof_qualified_class( [kindof_NSCopying randomClassMethod]; // in unrelated protocol } +// Make sure we don't emit warning about multiple methods found. +typedef int NSInteger; +@interface Foo : NSObject +- (NSString*)test; +@end +@interface Bar : NSObject +- (NSInteger)test; +@end +void test(__kindof Bar *kBar) { + [kBar test]; +} + // --------------------------------------------------------------------------- // __kindof within specialized types // --------------------------------------------------------------------------- From 105cf80a3319fabac832efd1c54ea72019d2693b Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Sat, 9 Apr 2016 18:59:48 +0000 Subject: [PATCH 452/742] ObjC kindof: check the context when inserting methods to global pool. To make kindof lookup work, we need to insert methods with different context into the global pool, even though they have the same siganture. Since diagnosis of availability is performed on the best candidate, which is often the first candidate from the global pool, we prioritize the methods that are unavaible or deprecated to the head of the list. Since we now have more methods in the global pool, we need to watch out for performance impact. rdar://25635831 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265877 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Sema/ObjCMethodList.h | 3 ++ lib/Sema/SemaDeclObjC.cpp | 39 +++++++++++++++++++++- test/FixIt/typo.m | 2 +- test/SemaObjC/kindof.m | 14 ++++++++ test/SemaObjC/multiple-method-names.m | 4 +-- test/SemaObjC/warn-strict-selector-match.m | 2 +- 6 files changed, 59 insertions(+), 5 deletions(-) diff --git a/include/clang/Sema/ObjCMethodList.h b/include/clang/Sema/ObjCMethodList.h index b618e38f88c..da59176e7d2 100644 --- a/include/clang/Sema/ObjCMethodList.h +++ b/include/clang/Sema/ObjCMethodList.h @@ -32,6 +32,9 @@ struct ObjCMethodList { ObjCMethodList() { } ObjCMethodList(ObjCMethodDecl *M) : MethodAndHasMoreThanOneDecl(M, 0) {} + ObjCMethodList(const ObjCMethodList &L) + : MethodAndHasMoreThanOneDecl(L.MethodAndHasMoreThanOneDecl), + NextAndExtraBits(L.NextAndExtraBits) {} ObjCMethodList *getNext() const { return NextAndExtraBits.getPointer(); } unsigned getBits() const { return NextAndExtraBits.getInt(); } diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index 1a44d2307b7..ba5edcf8977 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -3173,6 +3173,26 @@ bool Sema::MatchTwoMethodDeclarations(const ObjCMethodDecl *left, return true; } +static bool isMethodContextSameForKindofLookup(ObjCMethodDecl *Method, + ObjCMethodDecl *MethodInList) { + auto *MethodProtocol = dyn_cast(Method->getDeclContext()); + auto *MethodInListProtocol = + dyn_cast(MethodInList->getDeclContext()); + // If this method belongs to a protocol but the method in list does not, or + // vice versa, we say the context is not the same. + if ((MethodProtocol && !MethodInListProtocol) || + (!MethodProtocol && MethodInListProtocol)) + return false; + + if (MethodProtocol && MethodInListProtocol) + return true; + + ObjCInterfaceDecl *MethodInterface = Method->getClassInterface(); + ObjCInterfaceDecl *MethodInListInterface = + MethodInList->getClassInterface(); + return MethodInterface == MethodInListInterface; +} + void Sema::addMethodToGlobalList(ObjCMethodList *List, ObjCMethodDecl *Method) { // Record at the head of the list whether there were 0, 1, or >= 2 methods @@ -3191,13 +3211,17 @@ void Sema::addMethodToGlobalList(ObjCMethodList *List, // We've seen a method with this name, see if we have already seen this type // signature. + ObjCMethodList *Head = List; ObjCMethodList *Previous = List; for (; List; Previous = List, List = List->getNext()) { // If we are building a module, keep all of the methods. if (getLangOpts().CompilingModule) continue; - if (!MatchTwoMethodDeclarations(Method, List->getMethod())) { + // Looking for method with a type bound requires the correct context exists. + // We need to insert this method into the list if the context is different. + if (!MatchTwoMethodDeclarations(Method, List->getMethod()) || + !isMethodContextSameForKindofLookup(Method, List->getMethod())) { // Even if two method types do not match, we would like to say // there is more than one declaration so unavailability/deprecated // warning is not too noisy. @@ -3238,6 +3262,19 @@ void Sema::addMethodToGlobalList(ObjCMethodList *List, // We have a new signature for an existing method - add it. // This is extremely rare. Only 1% of Cocoa selectors are "overloaded". ObjCMethodList *Mem = BumpAlloc.Allocate(); + + // We tried to prioritize the list by putting deprecated and unavailable + // methods in the front. + if ((Method->isDeprecated() && !Head->getMethod()->isDeprecated()) || + (Method->isUnavailable() && + Head->getMethod()->getAvailability() < AR_Deprecated)) { + auto *List = new (Mem) ObjCMethodList(*Head); + // FIXME: should we clear the other bits in Head? + Head->setMethod(Method); + Head->setNext(List); + return; + } + Previous->setNext(new (Mem) ObjCMethodList(Method)); } diff --git a/test/FixIt/typo.m b/test/FixIt/typo.m index 381233f95c2..143d026b2be 100644 --- a/test/FixIt/typo.m +++ b/test/FixIt/typo.m @@ -103,7 +103,7 @@ - (int)method3:(id)x; @end @interface Sub : Super -- (int)method; +- (int)method; // expected-note{{also found}} @end @implementation Sub diff --git a/test/SemaObjC/kindof.m b/test/SemaObjC/kindof.m index 36c9b8f0a2b..95c7f993d17 100644 --- a/test/SemaObjC/kindof.m +++ b/test/SemaObjC/kindof.m @@ -275,6 +275,20 @@ void test(__kindof Bar *kBar) { [kBar test]; } +// Make sure we don't emit warning about no method found. +typedef signed char BOOL; +@interface A : NSObject +@property (readonly, getter=isActive) BOOL active; +@end +@interface B : NSObject +@property (getter=isActive, readonly) BOOL active; +@end +void foo() { + __kindof B *NSApp; + if ([NSApp isActive]) { + } +} + // --------------------------------------------------------------------------- // __kindof within specialized types // --------------------------------------------------------------------------- diff --git a/test/SemaObjC/multiple-method-names.m b/test/SemaObjC/multiple-method-names.m index 9fd83b208ab..a8707904b56 100644 --- a/test/SemaObjC/multiple-method-names.m +++ b/test/SemaObjC/multiple-method-names.m @@ -2,11 +2,11 @@ // PR22047 @interface Face0 -- (void)foo:(float)i; // expected-note {{using}} +- (void)foo:(float)i; // expected-note {{also found}} @end @interface Face1 -- (void)foo:(int)i __attribute__((unavailable)); +- (void)foo:(int)i __attribute__((unavailable)); // expected-note {{using}} @end @interface Face2 diff --git a/test/SemaObjC/warn-strict-selector-match.m b/test/SemaObjC/warn-strict-selector-match.m index 13e9bac462f..6b92cb805df 100644 --- a/test/SemaObjC/warn-strict-selector-match.m +++ b/test/SemaObjC/warn-strict-selector-match.m @@ -49,7 +49,7 @@ + (NTGridDataObject*)dataObject:(id)data; @end @implementation NTGridDataObject -- (id)initWithData:(id)data { +- (id)initWithData:(id)data { // expected-note {{also found}} return data; } + (NTGridDataObject*)dataObject:(id)data From b50f9c9c2cddbbb28e58282868d4d26b2e230060 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 1 Mar 2016 00:18:05 +0000 Subject: [PATCH 453/742] Infrastructure improvements to Clang attribute TableGen. This should make it easier to add new Attr subclasses. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262275 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Attr.h | 8 +- include/clang/Basic/AttrKinds.h | 6 +- lib/AST/ASTDumper.cpp | 2 - utils/TableGen/ClangAttrEmitter.cpp | 280 ++++++++++++++++++++-------- 4 files changed, 205 insertions(+), 91 deletions(-) diff --git a/include/clang/AST/Attr.h b/include/clang/AST/Attr.h index 8b80e9f6396..698909f0335 100644 --- a/include/clang/AST/Attr.h +++ b/include/clang/AST/Attr.h @@ -129,7 +129,8 @@ class InheritableAttr : public Attr { // Implement isa/cast/dyncast/etc. static bool classof(const Attr *A) { - return A->getKind() <= attr::LAST_INHERITABLE; + return A->getKind() >= attr::FirstInheritableAttr && + A->getKind() <= attr::LastInheritableAttr; } }; @@ -143,9 +144,8 @@ class InheritableParamAttr : public InheritableAttr { public: // Implement isa/cast/dyncast/etc. static bool classof(const Attr *A) { - // Relies on relative order of enum emission with respect to MS inheritance - // attrs. - return A->getKind() <= attr::LAST_INHERITABLE_PARAM; + return A->getKind() >= attr::FirstInheritableParamAttr && + A->getKind() <= attr::LastInheritableParamAttr; } }; diff --git a/include/clang/Basic/AttrKinds.h b/include/clang/Basic/AttrKinds.h index f0b0a6445d4..8f7394f59d4 100644 --- a/include/clang/Basic/AttrKinds.h +++ b/include/clang/Basic/AttrKinds.h @@ -22,10 +22,10 @@ namespace attr { // \brief A list of all the recognized kinds of attributes. enum Kind { #define ATTR(X) X, -#define LAST_INHERITABLE_ATTR(X) X, LAST_INHERITABLE = X, -#define LAST_INHERITABLE_PARAM_ATTR(X) X, LAST_INHERITABLE_PARAM = X, +#define ATTR_RANGE(CLASS, FIRST_NAME, LAST_NAME) \ + First##CLASS = FIRST_NAME, \ + Last##CLASS = LAST_NAME, #include "clang/Basic/AttrList.inc" - NUM_ATTRS }; } // end namespace attr diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index 9b7944271af..76401f56869 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -819,8 +819,6 @@ void ASTDumper::dumpAttr(const Attr *A) { switch (A->getKind()) { #define ATTR(X) case attr::X: OS << #X; break; #include "clang/Basic/AttrList.inc" - default: - llvm_unreachable("unexpected attribute kind"); } OS << "Attr"; } diff --git a/utils/TableGen/ClangAttrEmitter.cpp b/utils/TableGen/ClangAttrEmitter.cpp index a77f80c6d24..225a402ea3c 100644 --- a/utils/TableGen/ClangAttrEmitter.cpp +++ b/utils/TableGen/ClangAttrEmitter.cpp @@ -1716,8 +1716,6 @@ void EmitClangAttrImpl(RecordKeeper &Records, raw_ostream &OS) { OS << " return cast<" << R.getName() << "Attr>(this)->" << Method << ";\n"; } - OS << " case attr::NUM_ATTRS:\n"; - OS << " break;\n"; OS << " }\n"; OS << " llvm_unreachable(\"Unexpected attribute kind!\");\n"; OS << "}\n\n"; @@ -1736,20 +1734,10 @@ void EmitClangAttrImpl(RecordKeeper &Records, raw_ostream &OS) { } // end namespace clang -static void EmitAttrList(raw_ostream &OS, StringRef Class, +static void emitAttrList(raw_ostream &OS, StringRef Class, const std::vector &AttrList) { - auto i = AttrList.cbegin(), e = AttrList.cend(); - - if (i != e) { - // Move the end iterator back to emit the last attribute. - for(--e; i != e; ++i) { - if (!(*i)->getValueAsBit("ASTNode")) - continue; - - OS << Class << "(" << (*i)->getName() << ")\n"; - } - - OS << "LAST_" << Class << "(" << (*i)->getName() << ")\n\n"; + for (auto Cur : AttrList) { + OS << Class << "(" << Cur->getName() << ")\n"; } } @@ -1762,71 +1750,216 @@ static bool AttrHasPragmaSpelling(const Record *R) { }) != Spellings.end(); } -namespace clang { -// Emits the enumeration list for attributes. -void EmitClangAttrList(RecordKeeper &Records, raw_ostream &OS) { - emitSourceFileHeader("List of all attributes that Clang recognizes", OS); +namespace { + struct AttrClassDescriptor { + const char * const MacroName; + const char * const TableGenName; + }; +} - OS << "#ifndef LAST_ATTR\n"; - OS << "#define LAST_ATTR(NAME) ATTR(NAME)\n"; - OS << "#endif\n\n"; +static const AttrClassDescriptor AttrClassDescriptors[] = { + { "ATTR", "Attr" }, + { "INHERITABLE_ATTR", "InheritableAttr" }, + { "INHERITABLE_PARAM_ATTR", "InheritableParamAttr" } +}; - OS << "#ifndef INHERITABLE_ATTR\n"; - OS << "#define INHERITABLE_ATTR(NAME) ATTR(NAME)\n"; - OS << "#endif\n\n"; +static void emitDefaultDefine(raw_ostream &OS, StringRef name, + const char *superName) { + OS << "#ifndef " << name << "\n"; + OS << "#define " << name << "(NAME) "; + if (superName) OS << superName << "(NAME)"; + OS << "\n#endif\n\n"; +} - OS << "#ifndef LAST_INHERITABLE_ATTR\n"; - OS << "#define LAST_INHERITABLE_ATTR(NAME) INHERITABLE_ATTR(NAME)\n"; - OS << "#endif\n\n"; +namespace { + /// A class of attributes. + struct AttrClass { + const AttrClassDescriptor &Descriptor; + Record *TheRecord; + AttrClass *SuperClass = nullptr; + std::vector SubClasses; + std::vector Attrs; + + AttrClass(const AttrClassDescriptor &Descriptor, Record *R) + : Descriptor(Descriptor), TheRecord(R) {} + + void emitDefaultDefines(raw_ostream &OS) const { + // Default the macro unless this is a root class (i.e. Attr). + if (SuperClass) { + emitDefaultDefine(OS, Descriptor.MacroName, + SuperClass->Descriptor.MacroName); + } + } - OS << "#ifndef INHERITABLE_PARAM_ATTR\n"; - OS << "#define INHERITABLE_PARAM_ATTR(NAME) ATTR(NAME)\n"; - OS << "#endif\n\n"; + void emitUndefs(raw_ostream &OS) const { + OS << "#undef " << Descriptor.MacroName << "\n"; + } - OS << "#ifndef LAST_INHERITABLE_PARAM_ATTR\n"; - OS << "#define LAST_INHERITABLE_PARAM_ATTR(NAME)" - " INHERITABLE_PARAM_ATTR(NAME)\n"; - OS << "#endif\n\n"; + void emitAttrList(raw_ostream &OS) const { + for (auto SubClass : SubClasses) { + SubClass->emitAttrList(OS); + } - OS << "#ifndef PRAGMA_SPELLING_ATTR\n"; - OS << "#define PRAGMA_SPELLING_ATTR(NAME)\n"; - OS << "#endif\n\n"; + ::emitAttrList(OS, Descriptor.MacroName, Attrs); + } - OS << "#ifndef LAST_PRAGMA_SPELLING_ATTR\n"; - OS << "#define LAST_PRAGMA_SPELLING_ATTR(NAME) PRAGMA_SPELLING_ATTR(NAME)\n"; - OS << "#endif\n\n"; + void classifyAttrOnRoot(Record *Attr) { + bool result = classifyAttr(Attr); + assert(result && "failed to classify on root"); (void) result; + } - Record *InhClass = Records.getClass("InheritableAttr"); - Record *InhParamClass = Records.getClass("InheritableParamAttr"); - std::vector Attrs = Records.getAllDerivedDefinitions("Attr"), - NonInhAttrs, InhAttrs, InhParamAttrs, PragmaAttrs; + void emitAttrRange(raw_ostream &OS) const { + OS << "ATTR_RANGE(" << Descriptor.TableGenName + << ", " << getFirstAttr()->getName() + << ", " << getLastAttr()->getName() << ")\n"; + } + + private: + bool classifyAttr(Record *Attr) { + // Check all the subclasses. + for (auto SubClass : SubClasses) { + if (SubClass->classifyAttr(Attr)) + return true; + } + + // It's not more specific than this class, but it might still belong here. + if (Attr->isSubClassOf(TheRecord)) { + Attrs.push_back(Attr); + return true; + } + + return false; + } + + Record *getFirstAttr() const { + if (!SubClasses.empty()) + return SubClasses.front()->getFirstAttr(); + return Attrs.front(); + } + + Record *getLastAttr() const { + if (!Attrs.empty()) + return Attrs.back(); + return SubClasses.back()->getLastAttr(); + } + }; + + /// The entire hierarchy of attribute classes. + class AttrClassHierarchy { + std::vector> Classes; + public: + AttrClassHierarchy(RecordKeeper &Records) { + // Find records for all the classes. + for (auto &Descriptor : AttrClassDescriptors) { + Record *ClassRecord = Records.getClass(Descriptor.TableGenName); + AttrClass *Class = new AttrClass(Descriptor, ClassRecord); + Classes.emplace_back(Class); + } + + // Link up the hierarchy. + for (auto &Class : Classes) { + if (AttrClass *SuperClass = findSuperClass(Class->TheRecord)) { + Class->SuperClass = SuperClass; + SuperClass->SubClasses.push_back(Class.get()); + } + } + +#ifndef NDEBUG + for (auto i = Classes.begin(), e = Classes.end(); i != e; ++i) { + assert((i == Classes.begin()) == ((*i)->SuperClass == nullptr) && + "only the first class should be a root class!"); + } +#endif + } + + void emitDefaultDefines(raw_ostream &OS) const { + for (auto &Class : Classes) { + Class->emitDefaultDefines(OS); + } + } + + void emitUndefs(raw_ostream &OS) const { + for (auto &Class : Classes) { + Class->emitUndefs(OS); + } + } + + void emitAttrLists(raw_ostream &OS) const { + // Just start from the root class. + Classes[0]->emitAttrList(OS); + } + + void emitAttrRanges(raw_ostream &OS) const { + for (auto &Class : Classes) + Class->emitAttrRange(OS); + } + + void classifyAttr(Record *Attr) { + // Add the attribute to the root class. + Classes[0]->classifyAttrOnRoot(Attr); + } + + private: + AttrClass *findClassByRecord(Record *R) const { + for (auto &Class : Classes) { + if (Class->TheRecord == R) + return Class.get(); + } + return nullptr; + } + + AttrClass *findSuperClass(Record *R) const { + // TableGen flattens the superclass list, so we just need to walk it + // in reverse. + auto SuperClasses = R->getSuperClasses(); + for (signed i = 0, e = SuperClasses.size(); i != e; ++i) { + auto SuperClass = findClassByRecord(SuperClasses[e - i - 1].first); + if (SuperClass) return SuperClass; + } + return nullptr; + } + }; +} + +namespace clang { +// Emits the enumeration list for attributes. +void EmitClangAttrList(RecordKeeper &Records, raw_ostream &OS) { + emitSourceFileHeader("List of all attributes that Clang recognizes", OS); + + AttrClassHierarchy Hierarchy(Records); + + // Add defaulting macro definitions. + Hierarchy.emitDefaultDefines(OS); + emitDefaultDefine(OS, "PRAGMA_SPELLING_ATTR", nullptr); + + std::vector Attrs = Records.getAllDerivedDefinitions("Attr"); + std::vector PragmaAttrs; for (auto *Attr : Attrs) { if (!Attr->getValueAsBit("ASTNode")) continue; + // Add the attribute to the ad-hoc groups. if (AttrHasPragmaSpelling(Attr)) PragmaAttrs.push_back(Attr); - if (Attr->isSubClassOf(InhParamClass)) - InhParamAttrs.push_back(Attr); - else if (Attr->isSubClassOf(InhClass)) - InhAttrs.push_back(Attr); - else - NonInhAttrs.push_back(Attr); + // Place it in the hierarchy. + Hierarchy.classifyAttr(Attr); } - EmitAttrList(OS, "PRAGMA_SPELLING_ATTR", PragmaAttrs); - EmitAttrList(OS, "INHERITABLE_PARAM_ATTR", InhParamAttrs); - EmitAttrList(OS, "INHERITABLE_ATTR", InhAttrs); - EmitAttrList(OS, "ATTR", NonInhAttrs); + // Emit the main attribute list. + Hierarchy.emitAttrLists(OS); + + // Emit the ad hoc groups. + emitAttrList(OS, "PRAGMA_SPELLING_ATTR", PragmaAttrs); - OS << "#undef LAST_ATTR\n"; - OS << "#undef INHERITABLE_ATTR\n"; - OS << "#undef LAST_INHERITABLE_ATTR\n"; - OS << "#undef LAST_INHERITABLE_PARAM_ATTR\n"; - OS << "#undef LAST_PRAGMA_ATTR\n"; + // Emit the attribute ranges. + OS << "#ifdef ATTR_RANGE\n"; + Hierarchy.emitAttrRanges(OS); + OS << "#undef ATTR_RANGE\n"; + OS << "#endif\n"; + + Hierarchy.emitUndefs(OS); OS << "#undef PRAGMA_SPELLING_ATTR\n"; - OS << "#undef ATTR\n"; } // Emits the code to read an attribute from a precompiled header. @@ -1839,8 +1972,6 @@ void EmitClangAttrPCHRead(RecordKeeper &Records, raw_ostream &OS) { std::vector> Args; OS << " switch (Kind) {\n"; - OS << " default:\n"; - OS << " llvm_unreachable(\"Unknown attribute!\");\n"; for (const auto *Attr : Attrs) { const Record &R = *Attr; if (!R.getValueAsBit("ASTNode")) @@ -1880,9 +2011,6 @@ void EmitClangAttrPCHWrite(RecordKeeper &Records, raw_ostream &OS) { std::vector Attrs = Records.getAllDerivedDefinitions("Attr"), Args; OS << " switch (A->getKind()) {\n"; - OS << " default:\n"; - OS << " llvm_unreachable(\"Unknown attribute kind!\");\n"; - OS << " break;\n"; for (const auto *Attr : Attrs) { const Record &R = *Attr; if (!R.getValueAsBit("ASTNode")) @@ -2073,11 +2201,7 @@ void EmitClangAttrSpellingListIndex(RecordKeeper &Records, raw_ostream &OS) { emitSourceFileHeader("Code to translate different attribute spellings " "into internal identifiers", OS); - OS << - " switch (AttrKind) {\n" - " default:\n" - " llvm_unreachable(\"Unknown attribute kind!\");\n" - " break;\n"; + OS << " switch (AttrKind) {\n"; ParsedAttrMap Attrs = getParsedAttrList(Records); for (const auto &I : Attrs) { @@ -2157,9 +2281,7 @@ void EmitClangAttrASTVisitor(RecordKeeper &Records, raw_ostream &OS) { << " if (!A)\n" << " return true;\n" << "\n" - << " switch (A->getKind()) {\n" - << " default:\n" - << " return true;\n"; + << " switch (A->getKind()) {\n"; for (const auto *Attr : Attrs) { const Record &R = *Attr; @@ -2186,9 +2308,7 @@ void EmitClangAttrTemplateInstantiate(RecordKeeper &Records, raw_ostream &OS) { << "Attr *instantiateTemplateAttribute(const Attr *At, ASTContext &C, " << "Sema &S,\n" << " const MultiLevelTemplateArgumentList &TemplateArgs) {\n" - << " switch (At->getKind()) {\n" - << " default:\n" - << " break;\n"; + << " switch (At->getKind()) {\n"; for (const auto *Attr : Attrs) { const Record &R = *Attr; @@ -2786,11 +2906,7 @@ void EmitClangAttrParsedAttrKinds(RecordKeeper &Records, raw_ostream &OS) { void EmitClangAttrDump(RecordKeeper &Records, raw_ostream &OS) { emitSourceFileHeader("Attribute dumper", OS); - OS << - " switch (A->getKind()) {\n" - " default:\n" - " llvm_unreachable(\"Unknown attribute kind!\");\n" - " break;\n"; + OS << " switch (A->getKind()) {\n"; std::vector Attrs = Records.getAllDerivedDefinitions("Attr"), Args; for (const auto *Attr : Attrs) { const Record &R = *Attr; From 9e42907140585fb4bf01a75c1246efd2eb4c9e4c Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 1 Mar 2016 00:49:02 +0000 Subject: [PATCH 454/742] Generalize the consumed-parameter array on FunctionProtoType to allow arbitrary data to be associated with a parameter. Also, fix a bug where we apparently haven't been serializing this information for the last N years. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262278 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/ASTContext.h | 2 +- include/clang/AST/Type.h | 110 +++++++++++++++++++++++++------- lib/AST/ASTContext.cpp | 75 ++++++++++++---------- lib/AST/Type.cpp | 13 ++-- lib/Sema/SemaOverload.cpp | 5 +- lib/Sema/SemaType.cpp | 33 ++++++---- lib/Serialization/ASTReader.cpp | 11 ++++ lib/Serialization/ASTWriter.cpp | 8 ++- 8 files changed, 178 insertions(+), 79 deletions(-) diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index 438e676a979..fb5a64f5518 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -2260,7 +2260,7 @@ class ASTContext : public RefCountedBase { QualType mergeObjCGCQualifiers(QualType, QualType); - bool FunctionTypesMatchOnNSConsumedAttrs( + bool doFunctionTypesMatchOnExtParameterInfos( const FunctionProtoType *FromFunctionType, const FunctionProtoType *ToFunctionType); diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 2929037a87c..7b04058d7f9 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -3040,6 +3040,44 @@ class FunctionNoProtoType : public FunctionType, public llvm::FoldingSetNode { /// type. class FunctionProtoType : public FunctionType, public llvm::FoldingSetNode { public: + class ExtParameterInfo { + enum { + IsConsumed = 0x01, + }; + unsigned char Data; + public: + ExtParameterInfo() : Data(0) {} + + /// Is this parameter considered "consumed" by Objective-C ARC? + /// Consumed parameters must have retainable object type. + bool isConsumed() const { + return (Data & IsConsumed); + } + ExtParameterInfo withIsConsumed(bool consumed) const { + ExtParameterInfo copy = *this; + if (consumed) { + copy.Data |= IsConsumed; + } else { + copy.Data &= ~IsConsumed; + } + return copy; + } + + unsigned char getOpaqueValue() const { return Data; } + static ExtParameterInfo getFromOpaqueValue(unsigned char data) { + ExtParameterInfo result; + result.Data = data; + return result; + } + + friend bool operator==(ExtParameterInfo lhs, ExtParameterInfo rhs) { + return lhs.Data == rhs.Data; + } + friend bool operator!=(ExtParameterInfo lhs, ExtParameterInfo rhs) { + return lhs.Data != rhs.Data; + } + }; + struct ExceptionSpecInfo { ExceptionSpecInfo() : Type(EST_None), NoexceptExpr(nullptr), @@ -3067,11 +3105,11 @@ class FunctionProtoType : public FunctionType, public llvm::FoldingSetNode { struct ExtProtoInfo { ExtProtoInfo() : Variadic(false), HasTrailingReturn(false), TypeQuals(0), - RefQualifier(RQ_None), ConsumedParameters(nullptr) {} + RefQualifier(RQ_None), ExtParameterInfos(nullptr) {} ExtProtoInfo(CallingConv CC) : ExtInfo(CC), Variadic(false), HasTrailingReturn(false), TypeQuals(0), - RefQualifier(RQ_None), ConsumedParameters(nullptr) {} + RefQualifier(RQ_None), ExtParameterInfos(nullptr) {} ExtProtoInfo withExceptionSpec(const ExceptionSpecInfo &O) { ExtProtoInfo Result(*this); @@ -3085,7 +3123,7 @@ class FunctionProtoType : public FunctionType, public llvm::FoldingSetNode { unsigned char TypeQuals; RefQualifierKind RefQualifier; ExceptionSpecInfo ExceptionSpec; - const bool *ConsumedParameters; + const ExtParameterInfo *ExtParameterInfos; }; private: @@ -3112,8 +3150,8 @@ class FunctionProtoType : public FunctionType, public llvm::FoldingSetNode { /// The type of exception specification this function has. unsigned ExceptionSpecType : 4; - /// Whether this function has any consumed parameters. - unsigned HasAnyConsumedParams : 1; + /// Whether this function has extended parameter information. + unsigned HasExtParameterInfos : 1; /// Whether the function is variadic. unsigned Variadic : 1; @@ -3135,25 +3173,36 @@ class FunctionProtoType : public FunctionType, public llvm::FoldingSetNode { // instantiate this function type's exception specification, and the function // from which it should be instantiated. - // ConsumedParameters - A variable size array, following Exceptions - // and of length NumParams, holding flags indicating which parameters - // are consumed. This only appears if HasAnyConsumedParams is true. + // ExtParameterInfos - A variable size array, following the exception + // specification and of length NumParams, holding an ExtParameterInfo + // for each of the parameters. This only appears if HasExtParameterInfos + // is true. friend class ASTContext; // ASTContext creates these. - const bool *getConsumedParamsBuffer() const { - assert(hasAnyConsumedParams()); + const ExtParameterInfo *getExtParameterInfosBuffer() const { + assert(hasExtParameterInfos()); - // Find the end of the exceptions. - Expr *const *eh_end = reinterpret_cast(exception_end()); - if (getExceptionSpecType() == EST_ComputedNoexcept) - eh_end += 1; // NoexceptExpr - // The memory layout of these types isn't handled here, so - // hopefully this is never called for them? - assert(getExceptionSpecType() != EST_Uninstantiated && - getExceptionSpecType() != EST_Unevaluated); + // Find the end of the exception specification. + const char *ptr = reinterpret_cast(exception_begin()); + ptr += getExceptionSpecSize(); - return reinterpret_cast(eh_end); + return reinterpret_cast(ptr); + } + + size_t getExceptionSpecSize() const { + switch (getExceptionSpecType()) { + case EST_None: return 0; + case EST_DynamicNone: return 0; + case EST_MSAny: return 0; + case EST_BasicNoexcept: return 0; + case EST_Unparsed: return 0; + case EST_Dynamic: return getNumExceptions() * sizeof(QualType); + case EST_ComputedNoexcept: return sizeof(Expr*); + case EST_Uninstantiated: return 2 * sizeof(FunctionDecl*); + case EST_Unevaluated: return sizeof(FunctionDecl*); + } + llvm_unreachable("bad exception specification kind"); } public: @@ -3184,8 +3233,8 @@ class FunctionProtoType : public FunctionType, public llvm::FoldingSetNode { } else if (EPI.ExceptionSpec.Type == EST_Unevaluated) { EPI.ExceptionSpec.SourceDecl = getExceptionSpecDecl(); } - if (hasAnyConsumedParams()) - EPI.ConsumedParameters = getConsumedParamsBuffer(); + if (hasExtParameterInfos()) + EPI.ExtParameterInfos = getExtParameterInfosBuffer(); return EPI; } @@ -3300,11 +3349,24 @@ class FunctionProtoType : public FunctionType, public llvm::FoldingSetNode { return exception_begin() + NumExceptions; } - bool hasAnyConsumedParams() const { return HasAnyConsumedParams; } + bool hasExtParameterInfos() const { return HasExtParameterInfos; } + ArrayRef getExtParameterInfos() const { + assert(hasExtParameterInfos()); + return ArrayRef(getExtParameterInfosBuffer(), + getNumParams()); + } + + ExtParameterInfo getExtParameterInfo(unsigned I) const { + assert(I < getNumParams() && "parameter index out of range"); + if (hasExtParameterInfos()) + return getExtParameterInfosBuffer()[I]; + return ExtParameterInfo(); + } + bool isParamConsumed(unsigned I) const { assert(I < getNumParams() && "parameter index out of range"); - if (hasAnyConsumedParams()) - return getConsumedParamsBuffer()[I]; + if (hasExtParameterInfos()) + return getExtParameterInfosBuffer()[I].isConsumed(); return false; } diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 06fb46e2f7f..e631ff88889 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -2992,13 +2992,18 @@ ASTContext::getDependentSizedExtVectorType(QualType vecType, return QualType(New, 0); } +/// \brief Determine whether \p T is canonical as the result type of a function. +static bool isCanonicalResultType(QualType T) { + return T.isCanonical() && + (T.getObjCLifetime() == Qualifiers::OCL_None || + T.getObjCLifetime() == Qualifiers::OCL_ExplicitNone); +} + /// getFunctionNoProtoType - Return a K&R style C function type like 'int()'. /// QualType ASTContext::getFunctionNoProtoType(QualType ResultTy, const FunctionType::ExtInfo &Info) const { - const CallingConv CallConv = Info.getCC(); - // Unique functions, to guarantee there is only one function of a particular // structure. llvm::FoldingSetNodeID ID; @@ -3010,8 +3015,9 @@ ASTContext::getFunctionNoProtoType(QualType ResultTy, return QualType(FT, 0); QualType Canonical; - if (!ResultTy.isCanonical()) { - Canonical = getFunctionNoProtoType(getCanonicalType(ResultTy), Info); + if (!isCanonicalResultType(ResultTy)) { + Canonical = + getFunctionNoProtoType(getCanonicalFunctionResultType(ResultTy), Info); // Get the new insert position for the node we care about. FunctionNoProtoType *NewIP = @@ -3019,21 +3025,13 @@ ASTContext::getFunctionNoProtoType(QualType ResultTy, assert(!NewIP && "Shouldn't be in the map!"); (void)NewIP; } - FunctionProtoType::ExtInfo newInfo = Info.withCallingConv(CallConv); FunctionNoProtoType *New = new (*this, TypeAlignment) - FunctionNoProtoType(ResultTy, Canonical, newInfo); + FunctionNoProtoType(ResultTy, Canonical, Info); Types.push_back(New); FunctionNoProtoTypes.InsertNode(New, InsertPos); return QualType(New, 0); } -/// \brief Determine whether \p T is canonical as the result type of a function. -static bool isCanonicalResultType(QualType T) { - return T.isCanonical() && - (T.getObjCLifetime() == Qualifiers::OCL_None || - T.getObjCLifetime() == Qualifiers::OCL_ExplicitNone); -} - CanQualType ASTContext::getCanonicalFunctionResultType(QualType ResultType) const { CanQualType CanResultType = getCanonicalType(ResultType); @@ -3100,12 +3098,13 @@ ASTContext::getFunctionType(QualType ResultTy, ArrayRef ArgArray, // them for three variable size arrays at the end: // - parameter types // - exception types - // - consumed-arguments flags + // - extended parameter information // Instead of the exception types, there could be a noexcept // expression, or information used to resolve the exception // specification. size_t Size = sizeof(FunctionProtoType) + NumArgs * sizeof(QualType); + if (EPI.ExceptionSpec.Type == EST_Dynamic) { Size += EPI.ExceptionSpec.Exceptions.size() * sizeof(QualType); } else if (EPI.ExceptionSpec.Type == EST_ComputedNoexcept) { @@ -3115,8 +3114,16 @@ ASTContext::getFunctionType(QualType ResultTy, ArrayRef ArgArray, } else if (EPI.ExceptionSpec.Type == EST_Unevaluated) { Size += sizeof(FunctionDecl*); } - if (EPI.ConsumedParameters) - Size += NumArgs * sizeof(bool); + + // Put the ExtParameterInfos last. If all were equal, it would make + // more sense to put these before the exception specification, because + // it's much easier to skip past them compared to the elaborate switch + // required to skip the exception specification. However, all is not + // equal; ExtParameterInfos are used to model very uncommon features, + // and it's better not to burden the more common paths. + if (EPI.ExtParameterInfos) { + Size += NumArgs * sizeof(FunctionProtoType::ExtParameterInfo); + } FunctionProtoType *FTP = (FunctionProtoType*) Allocate(Size, TypeAlignment); FunctionProtoType::ExtProtoInfo newEPI = EPI; @@ -7483,8 +7490,7 @@ QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs, if (lproto->getTypeQuals() != rproto->getTypeQuals()) return QualType(); - if (LangOpts.ObjCAutoRefCount && - !FunctionTypesMatchOnNSConsumedAttrs(rproto, lproto)) + if (!doFunctionTypesMatchOnExtParameterInfos(rproto, lproto)) return QualType(); // Check parameter type compatibility @@ -7872,21 +7878,26 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, llvm_unreachable("Invalid Type::Class!"); } -bool ASTContext::FunctionTypesMatchOnNSConsumedAttrs( - const FunctionProtoType *FromFunctionType, - const FunctionProtoType *ToFunctionType) { - if (FromFunctionType->hasAnyConsumedParams() != - ToFunctionType->hasAnyConsumedParams()) +bool ASTContext::doFunctionTypesMatchOnExtParameterInfos( + const FunctionProtoType *firstFnType, + const FunctionProtoType *secondFnType) { + // Fast path: if the first type doesn't have ext parameter infos, + // we match if and only if they second type also doesn't have them. + if (!firstFnType->hasExtParameterInfos()) + return !secondFnType->hasExtParameterInfos(); + + // Otherwise, we can only match if the second type has them. + if (!secondFnType->hasExtParameterInfos()) return false; - FunctionProtoType::ExtProtoInfo FromEPI = - FromFunctionType->getExtProtoInfo(); - FunctionProtoType::ExtProtoInfo ToEPI = - ToFunctionType->getExtProtoInfo(); - if (FromEPI.ConsumedParameters && ToEPI.ConsumedParameters) - for (unsigned i = 0, n = FromFunctionType->getNumParams(); i != n; ++i) { - if (FromEPI.ConsumedParameters[i] != ToEPI.ConsumedParameters[i]) - return false; - } + + auto firstEPI = firstFnType->getExtParameterInfos(); + auto secondEPI = secondFnType->getExtParameterInfos(); + assert(firstEPI.size() == secondEPI.size()); + + for (size_t i = 0, n = firstEPI.size(); i != n; ++i) { + if (firstEPI[i] != secondEPI[i]) + return false; + } return true; } diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 721b8b21a73..a1b457b51a2 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -2673,7 +2673,7 @@ FunctionProtoType::FunctionProtoType(QualType result, ArrayRef params, NumParams(params.size()), NumExceptions(epi.ExceptionSpec.Exceptions.size()), ExceptionSpecType(epi.ExceptionSpec.Type), - HasAnyConsumedParams(epi.ConsumedParameters != nullptr), + HasExtParameterInfos(epi.ExtParameterInfos != nullptr), Variadic(epi.Variadic), HasTrailingReturn(epi.HasTrailingReturn) { assert(NumParams == params.size() && "function has too many parameters"); @@ -2739,10 +2739,11 @@ FunctionProtoType::FunctionProtoType(QualType result, ArrayRef params, slot[0] = epi.ExceptionSpec.SourceDecl; } - if (epi.ConsumedParameters) { - bool *consumedParams = const_cast(getConsumedParamsBuffer()); + if (epi.ExtParameterInfos) { + ExtParameterInfo *extParamInfos = + const_cast(getExtParameterInfosBuffer()); for (unsigned i = 0; i != NumParams; ++i) - consumedParams[i] = epi.ConsumedParameters[i]; + extParamInfos[i] = epi.ExtParameterInfos[i]; } } @@ -2862,9 +2863,9 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result, epi.ExceptionSpec.Type == EST_Unevaluated) { ID.AddPointer(epi.ExceptionSpec.SourceDecl->getCanonicalDecl()); } - if (epi.ConsumedParameters) { + if (epi.ExtParameterInfos) { for (unsigned i = 0; i != NumParams; ++i) - ID.AddBoolean(epi.ConsumedParameters[i]); + ID.AddInteger(epi.ExtParameterInfos[i].getOpaqueValue()); } epi.ExtInfo.Profile(ID); ID.AddBoolean(epi.HasTrailingReturn); diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 597661bbdea..15c849696e0 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -2547,9 +2547,8 @@ bool Sema::IsBlockPointerConversion(QualType FromType, QualType ToType, // Argument types are too different. Abort. return false; } - if (LangOpts.ObjCAutoRefCount && - !Context.FunctionTypesMatchOnNSConsumedAttrs(FromFunctionType, - ToFunctionType)) + if (!Context.doFunctionTypesMatchOnExtParameterInfos(FromFunctionType, + ToFunctionType)) return false; ConvertedType = ToType; diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index cf063669ec8..784fe945f5b 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -3979,9 +3979,9 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, SmallVector ParamTys; ParamTys.reserve(FTI.NumParams); - SmallVector ConsumedParameters; - ConsumedParameters.reserve(FTI.NumParams); - bool HasAnyConsumedParameters = false; + SmallVector + ExtParameterInfos(FTI.NumParams); + bool HasAnyInterestingExtParameterInfos = false; for (unsigned i = 0, e = FTI.NumParams; i != e; ++i) { ParmVarDecl *Param = cast(FTI.Params[i].Param); @@ -4039,17 +4039,16 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, } } - if (LangOpts.ObjCAutoRefCount) { - bool Consumed = Param->hasAttr(); - ConsumedParameters.push_back(Consumed); - HasAnyConsumedParameters |= Consumed; + if (LangOpts.ObjCAutoRefCount && Param->hasAttr()) { + ExtParameterInfos[i] = ExtParameterInfos[i].withIsConsumed(true); + HasAnyInterestingExtParameterInfos = true; } ParamTys.push_back(ParamTy); } - if (HasAnyConsumedParameters) - EPI.ConsumedParameters = ConsumedParameters.data(); + if (HasAnyInterestingExtParameterInfos) + EPI.ExtParameterInfos = ExtParameterInfos.data(); SmallVector Exceptions; SmallVector DynamicExceptions; @@ -5958,18 +5957,28 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state, } } - // Diagnose use of callee-cleanup calling convention on variadic functions. + // Diagnose use of variadic functions with calling conventions that + // don't support them (e.g. because they're callee-cleanup). + // We delay warning about this on unprototyped function declarations + // until after redeclaration checking, just in case we pick up a + // prototype that way. And apparently we also "delay" warning about + // unprototyped function types in general, despite not necessarily having + // much ability to diagnose it later. if (!supportsVariadicCall(CC)) { const FunctionProtoType *FnP = dyn_cast(fn); if (FnP && FnP->isVariadic()) { unsigned DiagID = diag::err_cconv_varargs; + // stdcall and fastcall are ignored with a warning for GCC and MS // compatibility. - if (CC == CC_X86StdCall || CC == CC_X86FastCall) + bool IsInvalid = true; + if (CC == CC_X86StdCall || CC == CC_X86FastCall) { DiagID = diag::warn_cconv_varargs; + IsInvalid = false; + } S.Diag(attr.getLoc(), DiagID) << FunctionType::getNameForCallConv(CC); - attr.setInvalid(); + if (IsInvalid) attr.setInvalid(); return true; } } diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 2316e570427..ca430ad384a 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -5379,6 +5379,17 @@ QualType ASTReader::readTypeRecord(unsigned Index) { for (unsigned I = 0; I != NumParams; ++I) ParamTypes.push_back(readType(*Loc.F, Record, Idx)); + SmallVector ExtParameterInfos; + if (Idx != Record.size()) { + for (unsigned I = 0; I != NumParams; ++I) + ExtParameterInfos.push_back( + FunctionProtoType::ExtParameterInfo + ::getFromOpaqueValue(Record[Idx++])); + EPI.ExtParameterInfos = ExtParameterInfos.data(); + } + + assert(Idx == Record.size()); + return Context.getFunctionType(ResultType, ParamTypes, EPI); } diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 89064bdcbee..b2da67d997a 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -238,8 +238,14 @@ void ASTTypeWriter::VisitFunctionProtoType(const FunctionProtoType *T) { for (unsigned I = 0, N = T->getNumParams(); I != N; ++I) Writer.AddTypeRef(T->getParamType(I), Record); + if (T->hasExtParameterInfos()) { + for (unsigned I = 0, N = T->getNumParams(); I != N; ++I) + Record.push_back(T->getExtParameterInfo(I).getOpaqueValue()); + } + if (T->isVariadic() || T->hasTrailingReturn() || T->getTypeQuals() || - T->getRefQualifier() || T->getExceptionSpecType() != EST_None) + T->getRefQualifier() || T->getExceptionSpecType() != EST_None || + T->hasExtParameterInfos()) AbbrevToUse = 0; Code = TYPE_FUNCTION_PROTO; From 412b2441b6ba620a7a764fe9401c45391548f0a1 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 1 Mar 2016 02:09:20 +0000 Subject: [PATCH 455/742] Add an llvm_unreachable back to the autogeneration of this covered switch. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262288 91177308-0d34-0410-b5e6-96231b3b80d8 --- utils/TableGen/ClangAttrEmitter.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/utils/TableGen/ClangAttrEmitter.cpp b/utils/TableGen/ClangAttrEmitter.cpp index 225a402ea3c..6f80eb6f061 100644 --- a/utils/TableGen/ClangAttrEmitter.cpp +++ b/utils/TableGen/ClangAttrEmitter.cpp @@ -2292,7 +2292,8 @@ void EmitClangAttrASTVisitor(RecordKeeper &Records, raw_ostream &OS) { << " return getDerived().Traverse" << R.getName() << "Attr(" << "cast<" << R.getName() << "Attr>(A));\n"; } - OS << " }\n"; // end case + OS << " }\n"; // end switch + OS << " llvm_unreachable(\"bad attribute kind\");\n"; OS << "}\n"; // end function OS << "#endif // ATTR_VISITOR_DECLS_ONLY\n"; } From ffe791726e74c987b0bef75a710ad9041b33b2de Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 1 Mar 2016 02:09:25 +0000 Subject: [PATCH 456/742] Fix the template instantiation of ExtParameterInfos; tests to follow. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262289 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Type.h | 5 ++ include/clang/Sema/Sema.h | 24 +++++++++- lib/Sema/SemaTemplateDeduction.cpp | 14 ++++-- lib/Sema/SemaTemplateInstantiate.cpp | 9 ++-- lib/Sema/SemaTemplateInstantiateDecl.cpp | 5 +- lib/Sema/TreeTransform.h | 60 ++++++++++++++++++++---- 6 files changed, 97 insertions(+), 20 deletions(-) diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 7b04058d7f9..0cd7e11e28f 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -3355,6 +3355,11 @@ class FunctionProtoType : public FunctionType, public llvm::FoldingSetNode { return ArrayRef(getExtParameterInfosBuffer(), getNumParams()); } + const ExtParameterInfo *getExtParameterInfosOrNull() const { + if (!hasExtParameterInfos()) + return nullptr; + return getExtParameterInfosBuffer(); + } ExtParameterInfo getExtParameterInfo(unsigned I) const { assert(I < getNumParams() && "parameter index out of range"); diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index ce14f8bf2b4..b86c52b90cb 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -6988,6 +6988,26 @@ class Sema { SavedPendingLocalImplicitInstantiations; }; + class ExtParameterInfoBuilder { + SmallVector Infos; + bool HasInteresting = false; + + public: + void set(unsigned index, FunctionProtoType::ExtParameterInfo info) { + assert(Infos.size() <= index); + Infos.resize(index); + Infos.push_back(info); + + if (!HasInteresting) + HasInteresting = (info != FunctionProtoType::ExtParameterInfo()); + } + + const FunctionProtoType::ExtParameterInfo * + getPointerOrNull(unsigned numParams) { + return (HasInteresting ? Infos.data() : nullptr); + } + }; + void PerformPendingInstantiations(bool LocalOnly = false); TypeSourceInfo *SubstType(TypeSourceInfo *T, @@ -7017,9 +7037,11 @@ class Sema { bool ExpectParameterPack); bool SubstParmTypes(SourceLocation Loc, ParmVarDecl **Params, unsigned NumParams, + const FunctionProtoType::ExtParameterInfo *ExtParamInfos, const MultiLevelTemplateArgumentList &TemplateArgs, SmallVectorImpl &ParamTypes, - SmallVectorImpl *OutParams = nullptr); + SmallVectorImpl *OutParams, + ExtParameterInfoBuilder &ParamInfos); ExprResult SubstExpr(Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs); diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index 71faafc6bc1..16e2d9f365d 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -2564,6 +2564,8 @@ Sema::SubstituteExplicitTemplateArguments( // Isolate our substituted parameters from our caller. LocalInstantiationScope InstScope(*this, /*MergeWithOuterScope*/true); + ExtParameterInfoBuilder ExtParamInfos; + // Instantiate the types of each of the function parameters given the // explicitly-specified template arguments. If the function has a trailing // return type, substitute it after the arguments to ensure we substitute @@ -2571,8 +2573,9 @@ Sema::SubstituteExplicitTemplateArguments( if (Proto->hasTrailingReturn()) { if (SubstParmTypes(Function->getLocation(), Function->param_begin(), Function->getNumParams(), + Proto->getExtParameterInfosOrNull(), MultiLevelTemplateArgumentList(*ExplicitArgumentList), - ParamTypes)) + ParamTypes, /*params*/ nullptr, ExtParamInfos)) return TDK_SubstitutionFailure; } @@ -2602,21 +2605,24 @@ Sema::SubstituteExplicitTemplateArguments( if (ResultType.isNull() || Trap.hasErrorOccurred()) return TDK_SubstitutionFailure; } - + // Instantiate the types of each of the function parameters given the // explicitly-specified template arguments if we didn't do so earlier. if (!Proto->hasTrailingReturn() && SubstParmTypes(Function->getLocation(), Function->param_begin(), Function->getNumParams(), + Proto->getExtParameterInfosOrNull(), MultiLevelTemplateArgumentList(*ExplicitArgumentList), - ParamTypes)) + ParamTypes, /*params*/ nullptr, ExtParamInfos)) return TDK_SubstitutionFailure; if (FunctionType) { + auto EPI = Proto->getExtProtoInfo(); + EPI.ExtParameterInfos = ExtParamInfos.getPointerOrNull(ParamTypes.size()); *FunctionType = BuildFunctionType(ResultType, ParamTypes, Function->getLocation(), Function->getDeclName(), - Proto->getExtProtoInfo()); + EPI); if (FunctionType->isNull() || Trap.hasErrorOccurred()) return TDK_SubstitutionFailure; } diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index fb7fc109d2e..000eb6f06d6 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -1720,9 +1720,11 @@ ParmVarDecl *Sema::SubstParmVarDecl(ParmVarDecl *OldParm, /// from such a substitution. bool Sema::SubstParmTypes(SourceLocation Loc, ParmVarDecl **Params, unsigned NumParams, + const FunctionProtoType::ExtParameterInfo *ExtParamInfos, const MultiLevelTemplateArgumentList &TemplateArgs, SmallVectorImpl &ParamTypes, - SmallVectorImpl *OutParams) { + SmallVectorImpl *OutParams, + ExtParameterInfoBuilder &ParamInfos) { assert(!ActiveTemplateInstantiations.empty() && "Cannot perform an instantiation without some context on the " "instantiation stack"); @@ -1730,8 +1732,9 @@ bool Sema::SubstParmTypes(SourceLocation Loc, TemplateInstantiator Instantiator(*this, TemplateArgs, Loc, DeclarationName()); return Instantiator.TransformFunctionTypeParams(Loc, Params, NumParams, - nullptr, ParamTypes, - OutParams); + nullptr, ExtParamInfos, + ParamTypes, OutParams, + ParamInfos); } /// \brief Perform substitution on the base class specifiers of the diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 2c5a18c2b73..854c6ff9482 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -3120,9 +3120,10 @@ TemplateDeclInstantiator::SubstFunctionType(FunctionDecl *D, // In this case, we'll just go instantiate the ParmVarDecls that we // synthesized in the method declaration. SmallVector ParamTypes; + Sema::ExtParameterInfoBuilder ExtParamInfos; if (SemaRef.SubstParmTypes(D->getLocation(), D->param_begin(), - D->getNumParams(), TemplateArgs, ParamTypes, - &Params)) + D->getNumParams(), nullptr, TemplateArgs, + ParamTypes, &Params, ExtParamInfos)) return nullptr; } diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index 2092b579a47..d5337784f0d 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -607,8 +607,10 @@ class TreeTransform { bool TransformFunctionTypeParams(SourceLocation Loc, ParmVarDecl **Params, unsigned NumParams, const QualType *ParamTypes, + const FunctionProtoType::ExtParameterInfo *ParamInfos, SmallVectorImpl &PTypes, - SmallVectorImpl *PVars); + SmallVectorImpl *PVars, + Sema::ExtParameterInfoBuilder &PInfos); /// \brief Transforms a single function-type parameter. Return null /// on error. @@ -4608,8 +4610,10 @@ bool TreeTransform:: TransformFunctionTypeParams(SourceLocation Loc, ParmVarDecl **Params, unsigned NumParams, const QualType *ParamTypes, + const FunctionProtoType::ExtParameterInfo *ParamInfos, SmallVectorImpl &OutParamTypes, - SmallVectorImpl *PVars) { + SmallVectorImpl *PVars, + Sema::ExtParameterInfoBuilder &PInfos) { int indexAdjustment = 0; for (unsigned i = 0; i != NumParams; ++i) { @@ -4658,6 +4662,8 @@ bool TreeTransform:: if (!NewParm) return true; + if (ParamInfos) + PInfos.set(OutParamTypes.size(), ParamInfos[i]); OutParamTypes.push_back(NewParm->getType()); if (PVars) PVars->push_back(NewParm); @@ -4675,6 +4681,8 @@ bool TreeTransform:: if (!NewParm) return true; + if (ParamInfos) + PInfos.set(OutParamTypes.size(), ParamInfos[i]); OutParamTypes.push_back(NewParm->getType()); if (PVars) PVars->push_back(NewParm); @@ -4705,6 +4713,8 @@ bool TreeTransform:: if (!NewParm) return true; + if (ParamInfos) + PInfos.set(OutParamTypes.size(), ParamInfos[i]); OutParamTypes.push_back(NewParm->getType()); if (PVars) PVars->push_back(NewParm); @@ -4744,6 +4754,8 @@ bool TreeTransform:: if (NewType.isNull()) return true; + if (ParamInfos) + PInfos.set(OutParamTypes.size(), ParamInfos[i]); OutParamTypes.push_back(NewType); if (PVars) PVars->push_back(nullptr); @@ -4761,6 +4773,8 @@ bool TreeTransform:: if (NewType.isNull()) return true; + if (ParamInfos) + PInfos.set(OutParamTypes.size(), ParamInfos[i]); OutParamTypes.push_back(NewType); if (PVars) PVars->push_back(nullptr); @@ -4783,6 +4797,8 @@ bool TreeTransform:: NewType = getSema().Context.getPackExpansionType(NewType, NumExpansions); + if (ParamInfos) + PInfos.set(OutParamTypes.size(), ParamInfos[i]); OutParamTypes.push_back(NewType); if (PVars) PVars->push_back(nullptr); @@ -4817,6 +4833,7 @@ template template QualType TreeTransform::TransformFunctionProtoType( TypeLocBuilder &TLB, FunctionProtoTypeLoc TL, CXXRecordDecl *ThisContext, unsigned ThisTypeQuals, Fn TransformExceptionSpec) { + // Transform the parameters and return type. // // We are required to instantiate the params and return type in source order. @@ -4826,6 +4843,7 @@ QualType TreeTransform::TransformFunctionProtoType( // SmallVector ParamTypes; SmallVector ParamDecls; + Sema::ExtParameterInfoBuilder ExtParamInfos; const FunctionProtoType *T = TL.getTypePtr(); QualType ResultType; @@ -4833,7 +4851,9 @@ QualType TreeTransform::TransformFunctionProtoType( if (T->hasTrailingReturn()) { if (getDerived().TransformFunctionTypeParams( TL.getBeginLoc(), TL.getParmArray(), TL.getNumParams(), - TL.getTypePtr()->param_type_begin(), ParamTypes, &ParamDecls)) + TL.getTypePtr()->param_type_begin(), + T->getExtParameterInfosOrNull(), + ParamTypes, &ParamDecls, ExtParamInfos)) return QualType(); { @@ -4857,7 +4877,9 @@ QualType TreeTransform::TransformFunctionProtoType( if (getDerived().TransformFunctionTypeParams( TL.getBeginLoc(), TL.getParmArray(), TL.getNumParams(), - TL.getTypePtr()->param_type_begin(), ParamTypes, &ParamDecls)) + TL.getTypePtr()->param_type_begin(), + T->getExtParameterInfosOrNull(), + ParamTypes, &ParamDecls, ExtParamInfos)) return QualType(); } @@ -4867,8 +4889,19 @@ QualType TreeTransform::TransformFunctionProtoType( if (TransformExceptionSpec(EPI.ExceptionSpec, EPIChanged)) return QualType(); - // FIXME: Need to transform ConsumedParameters for variadic template - // expansion. + // Handle extended parameter information. + if (auto NewExtParamInfos = + ExtParamInfos.getPointerOrNull(ParamTypes.size())) { + if (!EPI.ExtParameterInfos || + llvm::makeArrayRef(EPI.ExtParameterInfos, TL.getNumParams()) + != llvm::makeArrayRef(NewExtParamInfos, ParamTypes.size())) { + EPIChanged = true; + } + EPI.ExtParameterInfos = NewExtParamInfos; + } else if (EPI.ExtParameterInfos) { + EPIChanged = true; + EPI.ExtParameterInfos = nullptr; + } QualType Result = TL.getType(); if (getDerived().AlwaysRebuild() || ResultType != T->getReturnType() || @@ -11062,22 +11095,29 @@ TreeTransform::TransformBlockExpr(BlockExpr *E) { SmallVector params; SmallVector paramTypes; + const FunctionProtoType *exprFunctionType = E->getFunctionType(); + // Parameter substitution. + Sema::ExtParameterInfoBuilder extParamInfos; if (getDerived().TransformFunctionTypeParams(E->getCaretLocation(), oldBlock->param_begin(), oldBlock->param_size(), - nullptr, paramTypes, ¶ms)) { + nullptr, + exprFunctionType->getExtParameterInfosOrNull(), + paramTypes, ¶ms, + extParamInfos)) { getSema().ActOnBlockError(E->getCaretLocation(), /*Scope=*/nullptr); return ExprError(); } - const FunctionProtoType *exprFunctionType = E->getFunctionType(); QualType exprResultType = getDerived().TransformType(exprFunctionType->getReturnType()); + auto epi = exprFunctionType->getExtProtoInfo(); + epi.ExtParameterInfos = extParamInfos.getPointerOrNull(paramTypes.size()); + QualType functionType = - getDerived().RebuildFunctionProtoType(exprResultType, paramTypes, - exprFunctionType->getExtProtoInfo()); + getDerived().RebuildFunctionProtoType(exprResultType, paramTypes, epi); blockScope->FunctionType = functionType; // Set the parameters on the block decl. From ea0a8df8319e1fd3cbe88e49a8ae61f89f688018 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 1 Mar 2016 06:27:40 +0000 Subject: [PATCH 457/742] Better comments for ExtParameterInfo. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262308 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Type.h | 24 ++++++++++++++++++++++++ include/clang/Sema/Sema.h | 11 +++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 0cd7e11e28f..4e26236024b 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -3040,6 +3040,25 @@ class FunctionNoProtoType : public FunctionType, public llvm::FoldingSetNode { /// type. class FunctionProtoType : public FunctionType, public llvm::FoldingSetNode { public: + /// Interesting information about a specific parameter that can't simply + /// be reflected in parameter's type. + /// + /// It makes sense to model language features this way when there's some + /// sort of parameter-specific override (such as an attribute) that + /// affects how the function is called. For example, the ARC ns_consumed + /// attribute changes whether a parameter is passed at +0 (the default) + /// or +1 (ns_consumed). This must be reflected in the function type, + /// but isn't really a change to the parameter type. + /// + /// One serious disadvantage of modelling language features this way is + /// that they generally do not work with language features that attempt + /// to destructure types. For example, template argument deduction will + /// not be able to match a parameter declared as + /// T (*)(U) + /// against an argument of type + /// void (*)(__attribute__((ns_consumed)) id) + /// because the substitution of T=void, U=id into the former will + /// not produce the latter. class ExtParameterInfo { enum { IsConsumed = 0x01, @@ -3349,12 +3368,17 @@ class FunctionProtoType : public FunctionType, public llvm::FoldingSetNode { return exception_begin() + NumExceptions; } + /// Is there any interesting extra information for any of the parameters + /// of this function type? bool hasExtParameterInfos() const { return HasExtParameterInfos; } ArrayRef getExtParameterInfos() const { assert(hasExtParameterInfos()); return ArrayRef(getExtParameterInfosBuffer(), getNumParams()); } + /// Return a pointer to the beginning of the array of extra parameter + /// information, if present, or else null if none of the parameters + /// carry it. This is equivalent to getExtProtoInfo().ExtParameterInfos. const ExtParameterInfo *getExtParameterInfosOrNull() const { if (!hasExtParameterInfos()) return nullptr; diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index b86c52b90cb..7958ff2743b 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -6988,11 +6988,14 @@ class Sema { SavedPendingLocalImplicitInstantiations; }; + /// A helper class for building up ExtParameterInfos. class ExtParameterInfoBuilder { - SmallVector Infos; + SmallVector Infos; bool HasInteresting = false; public: + /// Set the ExtParameterInfo for the parameter at the given index, + /// void set(unsigned index, FunctionProtoType::ExtParameterInfo info) { assert(Infos.size() <= index); Infos.resize(index); @@ -7002,9 +7005,13 @@ class Sema { HasInteresting = (info != FunctionProtoType::ExtParameterInfo()); } + /// Return a pointer (suitable for setting in an ExtProtoInfo) to the + /// ExtParameterInfo array we've built up. const FunctionProtoType::ExtParameterInfo * getPointerOrNull(unsigned numParams) { - return (HasInteresting ? Infos.data() : nullptr); + if (!HasInteresting) return nullptr; + Infos.resize(numParams); + return Infos.data(); } }; From b9f9d81c7ad435b3124ce101616d562684e3719b Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 1 Mar 2016 06:54:30 +0000 Subject: [PATCH 458/742] Test template instantiation of ns_consumed and ns_returns_retained. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262311 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/SemaObjCXX/arc-templates.mm | 126 ++++++++++++++++++++++++++++++- 1 file changed, 125 insertions(+), 1 deletion(-) diff --git a/test/SemaObjCXX/arc-templates.mm b/test/SemaObjCXX/arc-templates.mm index ebede6404f9..81425985e62 100644 --- a/test/SemaObjCXX/arc-templates.mm +++ b/test/SemaObjCXX/arc-templates.mm @@ -1,4 +1,7 @@ -// RUN: %clang_cc1 -fobjc-runtime-has-weak -fsyntax-only -fobjc-arc -verify -fblocks %s +// RUN: %clang_cc1 -fobjc-runtime-has-weak -fsyntax-only -fobjc-arc -verify -fblocks -std=c++11 %s + +#define CONSUMED __attribute__((ns_consumed)) +#define PRODUCED __attribute__((ns_returns_retained)) @interface A @end @@ -318,3 +321,124 @@ void foo() { double &dr = (f)(unsafe); } } + +namespace consumed { + void take_yes_no(void (&)(id CONSUMED, id)); // expected-note 2 {{candidate function not viable}} + void take_no_yes(void (&)(id, CONSUMED id)); // expected-note 2 {{candidate function not viable}} + void take_yes_yes(void (&)(CONSUMED id, CONSUMED id)); // expected-note 2 {{candidate function not viable}} + + template void consumes_first(id CONSUMED, As...); + void test1() { + take_yes_no(consumes_first); + take_no_yes(consumes_first); // expected-error {{no matching function}} + take_yes_yes(consumes_first); // expected-error {{no matching function}} + } + + template void consumes_rest(id, CONSUMED As...); + void test2() { + take_yes_no(consumes_rest); // expected-error {{no matching function}} + take_no_yes(consumes_rest); + take_yes_yes(consumes_rest); // expected-error {{no matching function}} + } + + template void consumes_two(CONSUMED T, CONSUMED U); + void test3() { + take_yes_no(consumes_two); // expected-error {{no matching function}} + take_no_yes(consumes_two); // expected-error {{no matching function}} + take_yes_yes(consumes_two); + } +} + +namespace consumed_nested { + void take_yes_no(void (&)(id CONSUMED, id)); // expected-note 4 {{candidate function not viable}} + void take_no_yes(void (&)(id, CONSUMED id)); // expected-note 4 {{candidate function not viable}} + void take_yes_yes(void (&)(CONSUMED id, CONSUMED id)); // expected-note 4 {{candidate function not viable}} + + template struct consumes_first { + template static void fn(id CONSUMED, As...); + }; + void test1() { + take_yes_no(consumes_first<1>::fn); + take_no_yes(consumes_first<2>::fn); // expected-error {{no matching function}} + take_yes_yes(consumes_first<3>::fn); // expected-error {{no matching function}} + take_yes_no(consumes_first<4>::fn); + take_no_yes(consumes_first<5>::fn); // expected-error {{no matching function}} + take_yes_yes(consumes_first<6>::fn); // expected-error {{no matching function}} + } + + template struct consumes_rest { + template static void fn(id, CONSUMED As...); + }; + void test2() { + take_yes_no(consumes_rest<1>::fn); // expected-error {{no matching function}} + take_no_yes(consumes_rest<2>::fn); + take_yes_yes(consumes_rest<3>::fn); // expected-error {{no matching function}} + take_yes_no(consumes_rest<4>::fn); // expected-error {{no matching function}} + take_no_yes(consumes_rest<5>::fn); + take_yes_yes(consumes_rest<6>::fn); // expected-error {{no matching function}} + } + + template struct consumes_two { + template static void fn(CONSUMED T, CONSUMED U); + }; + void test3() { + take_yes_no(consumes_two<1>::fn); // expected-error {{no matching function}} + take_no_yes(consumes_two<2>::fn); // expected-error {{no matching function}} + take_yes_yes(consumes_two<3>::fn); + take_yes_no(consumes_two<1>::fn); // expected-error {{no matching function}} + take_no_yes(consumes_two<2>::fn); // expected-error {{no matching function}} + take_yes_yes(consumes_two<3>::fn); + } +} + +namespace produced { + void take_yes(PRODUCED id (&)()); // expected-note 2 {{candidate function not viable}} + void take_no(id (&)()); // expected-note 2 {{candidate function not viable}} + + template T non_produces1(); + template T non_produces2(); + template T non_produces3(); + template T non_produces4(); + void test1() { + take_yes(non_produces1); // expected-error {{no matching function}} + take_yes(non_produces2); // expected-error {{no matching function}} + take_no(non_produces3); + take_no(non_produces4); + } + + template PRODUCED T produces1(); + template PRODUCED T produces2(); + template PRODUCED T produces3(); + template PRODUCED T produces4(); + void test2() { + take_yes(produces1); + take_yes(produces2); + take_no(produces3); // expected-error {{no matching function}} + take_no(produces4); // expected-error {{no matching function}} + } +} + +namespace produced_nested { + void take_yes(PRODUCED id (&)()); // expected-note 2 {{candidate function not viable}} + void take_no(id (&)()); // expected-note 2 {{candidate function not viable}} + + template struct non_produces { + template static T fn(); + }; + void test1() { + take_yes(non_produces<1>::fn); // expected-error {{no matching function}} + take_yes(non_produces<2>::fn); // expected-error {{no matching function}} + take_no(non_produces<3>::fn); + take_no(non_produces<4>::fn); + } + + template struct produces { + template static PRODUCED T fn(); + }; + void test2() { + take_yes(produces<1>::fn); + take_yes(produces<2>::fn); + take_no(produces<3>::fn); // expected-error {{no matching function}} + take_no(produces<4>::fn); // expected-error {{no matching function}} + } +} From 3d304d8ae333a75dbdea769e26e66409d52b9004 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 1 Mar 2016 22:18:03 +0000 Subject: [PATCH 459/742] Mangle extended qualifiers in the proper order and mangle the ARC ownership-convention function type modifications. According to the Itanium ABI, vendor extended qualifiers are supposed to be mangled in reverse-alphabetical order before any CVR qualifiers. The ARC function type conventions are plausibly order-significant (they are associated with the function type), which permits us to ignore the need to correctly inter-order them with any other vendor qualifiers on the parameter and return types. Implementing these rules correctly is technically an ABI break. Apple is comfortable with the risk of incompatibility here for the ARC features, and I believe that address-space qualification is still uncommon enough to allow us to adopt the conforming rule without serious risk. Still, targets which make heavy use of address space qualification may want to revert to the non-conforming order. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262414 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ItaniumMangle.cpp | 124 ++++++++++++++++++----- test/CodeGenCXX/mangle-address-space.cpp | 3 + test/CodeGenObjCXX/arc-attrs.mm | 10 +- test/CodeGenObjCXX/arc-mangle.mm | 13 ++- test/CodeGenObjCXX/arc-move.mm | 4 +- 5 files changed, 120 insertions(+), 34 deletions(-) diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index 3f6b682f238..269278b96dd 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -364,6 +364,7 @@ class CXXNameMangler { StringRef Prefix = ""); void mangleOperatorName(DeclarationName Name, unsigned Arity); void mangleOperatorName(OverloadedOperatorKind OO, unsigned Arity); + void mangleVendorQualifier(StringRef qualifier); void mangleQualifiers(Qualifiers Quals); void mangleRefQualifier(RefQualifierKind RefQualifier); @@ -377,7 +378,10 @@ class CXXNameMangler { void mangleType(const TagType*); void mangleType(TemplateName); - void mangleBareFunctionType(const FunctionType *T, bool MangleReturnType, + static StringRef getCallingConvQualifierName(CallingConv CC); + void mangleExtParameterInfo(FunctionProtoType::ExtParameterInfo info); + void mangleExtFunctionInfo(const FunctionType *T); + void mangleBareFunctionType(const FunctionProtoType *T, bool MangleReturnType, const FunctionDecl *FD = nullptr); void mangleNeonVectorType(const VectorType *T); void mangleAArch64NeonVectorType(const VectorType *T); @@ -523,7 +527,7 @@ void CXXNameMangler::mangleFunctionEncoding(const FunctionDecl *FD) { FD = PrimaryTemplate->getTemplatedDecl(); } - mangleBareFunctionType(FD->getType()->getAs(), + mangleBareFunctionType(FD->getType()->castAs(), MangleReturnType, FD); } @@ -1767,14 +1771,9 @@ CXXNameMangler::mangleOperatorName(OverloadedOperatorKind OO, unsigned Arity) { } void CXXNameMangler::mangleQualifiers(Qualifiers Quals) { - // ::= [r] [V] [K] # restrict (C99), volatile, const - if (Quals.hasRestrict()) - Out << 'r'; - if (Quals.hasVolatile()) - Out << 'V'; - if (Quals.hasConst()) - Out << 'K'; + // Vendor qualifiers come first. + // Address space qualifiers start with an ordinary letter. if (Quals.hasAddressSpace()) { // Address space extension: // @@ -1802,10 +1801,10 @@ void CXXNameMangler::mangleQualifiers(Qualifiers Quals) { case LangAS::cuda_shared: ASString = "CUshared"; break; } } - Out << 'U' << ASString.size() << ASString; + mangleVendorQualifier(ASString); } - - StringRef LifetimeName; + + // The ARC ownership qualifiers start with underscores. switch (Quals.getObjCLifetime()) { // Objective-C ARC Extension: // @@ -1816,15 +1815,15 @@ void CXXNameMangler::mangleQualifiers(Qualifiers Quals) { break; case Qualifiers::OCL_Weak: - LifetimeName = "__weak"; + mangleVendorQualifier("__weak"); break; case Qualifiers::OCL_Strong: - LifetimeName = "__strong"; + mangleVendorQualifier("__strong"); break; case Qualifiers::OCL_Autoreleasing: - LifetimeName = "__autoreleasing"; + mangleVendorQualifier("__autoreleasing"); break; case Qualifiers::OCL_ExplicitNone: @@ -1837,8 +1836,18 @@ void CXXNameMangler::mangleQualifiers(Qualifiers Quals) { // in any type signatures that need to be mangled. break; } - if (!LifetimeName.empty()) - Out << 'U' << LifetimeName.size() << LifetimeName; + + // ::= [r] [V] [K] # restrict (C99), volatile, const + if (Quals.hasRestrict()) + Out << 'r'; + if (Quals.hasVolatile()) + Out << 'V'; + if (Quals.hasConst()) + Out << 'K'; +} + +void CXXNameMangler::mangleVendorQualifier(StringRef name) { + Out << 'U' << name.size() << name; } void CXXNameMangler::mangleRefQualifier(RefQualifierKind RefQualifier) { @@ -2137,10 +2146,63 @@ void CXXNameMangler::mangleType(const BuiltinType *T) { } } +StringRef CXXNameMangler::getCallingConvQualifierName(CallingConv CC) { + switch (CC) { + case CC_C: + return ""; + + case CC_X86StdCall: + case CC_X86FastCall: + case CC_X86ThisCall: + case CC_X86VectorCall: + case CC_X86Pascal: + case CC_X86_64Win64: + case CC_X86_64SysV: + case CC_AAPCS: + case CC_AAPCS_VFP: + case CC_IntelOclBicc: + case CC_SpirFunction: + case CC_SpirKernel: + // FIXME: we should be mangling all of the above. + return ""; + } + llvm_unreachable("bad calling convention"); +} + +void CXXNameMangler::mangleExtFunctionInfo(const FunctionType *T) { + // Fast path. + if (T->getExtInfo() == FunctionType::ExtInfo()) + return; + + // Vendor-specific qualifiers are emitted in reverse alphabetical order. + // This will get more complicated in the future if we mangle other + // things here; but for now, since we mangle ns_returns_retained as + // a qualifier on the result type, we can get away with this: + StringRef CCQualifier = getCallingConvQualifierName(T->getExtInfo().getCC()); + if (!CCQualifier.empty()) + mangleVendorQualifier(CCQualifier); + + // FIXME: regparm + // FIXME: noreturn +} + +void +CXXNameMangler::mangleExtParameterInfo(FunctionProtoType::ExtParameterInfo PI) { + // Vendor-specific qualifiers are emitted in reverse alphabetical order. + + // Note that these are *not* substitution candidates. Demanglers might + // have trouble with this if the parameter type is fully substituted. + + if (PI.isConsumed()) + Out << "U11ns_consumed"; +} + // ::= // ::= [] F [Y] // [] E void CXXNameMangler::mangleType(const FunctionProtoType *T) { + mangleExtFunctionInfo(T); + // Mangle CV-qualifiers, if present. These are 'this' qualifiers, // e.g. "const" in "int (A::*)() const". mangleQualifiers(Qualifiers::fromCVRMask(T->getTypeQuals())); @@ -2173,12 +2235,9 @@ void CXXNameMangler::mangleType(const FunctionNoProtoType *T) { Out << 'E'; } -void CXXNameMangler::mangleBareFunctionType(const FunctionType *T, +void CXXNameMangler::mangleBareFunctionType(const FunctionProtoType *Proto, bool MangleReturnType, const FunctionDecl *FD) { - // We should never be mangling something without a prototype. - const FunctionProtoType *Proto = cast(T); - // Record that we're in a function type. See mangleFunctionParam // for details on what we're trying to achieve here. FunctionTypeDepthState saved = FunctionTypeDepth.push(); @@ -2186,7 +2245,20 @@ void CXXNameMangler::mangleBareFunctionType(const FunctionType *T, // ::= + if (MangleReturnType) { FunctionTypeDepth.enterResultType(); - mangleType(Proto->getReturnType()); + + // Mangle ns_returns_retained as an order-sensitive qualifier here. + if (Proto->getExtInfo().getProducesResult()) + mangleVendorQualifier("ns_returns_retained"); + + // Mangle the return type without any direct ARC ownership qualifiers. + QualType ReturnTy = Proto->getReturnType(); + if (ReturnTy.getObjCLifetime()) { + auto SplitReturnTy = ReturnTy.split(); + SplitReturnTy.Quals.removeObjCLifetime(); + ReturnTy = getASTContext().getQualifiedType(SplitReturnTy); + } + mangleType(ReturnTy); + FunctionTypeDepth.leaveResultType(); } @@ -2200,7 +2272,13 @@ void CXXNameMangler::mangleBareFunctionType(const FunctionType *T, assert(!FD || FD->getNumParams() == Proto->getNumParams()); for (unsigned I = 0, E = Proto->getNumParams(); I != E; ++I) { - const auto &ParamTy = Proto->getParamType(I); + // Mangle extended parameter info as order-sensitive qualifiers here. + if (Proto->hasExtParameterInfos()) { + mangleExtParameterInfo(Proto->getExtParameterInfo(I)); + } + + // Mangle the type. + QualType ParamTy = Proto->getParamType(I); mangleType(Context.getASTContext().getSignatureParameterType(ParamTy)); if (FD) { diff --git a/test/CodeGenCXX/mangle-address-space.cpp b/test/CodeGenCXX/mangle-address-space.cpp index f18480de83d..cd10384594e 100644 --- a/test/CodeGenCXX/mangle-address-space.cpp +++ b/test/CodeGenCXX/mangle-address-space.cpp @@ -10,3 +10,6 @@ typedef OpaqueType __attribute__((address_space(100))) * OpaqueTypePtr; // CHECK-LABEL: define {{.*}}void @_Z2f0PU5AS10010OpaqueType void f0(OpaqueTypePtr) { } + +// CHECK-LABEL: define {{.*}}void @_Z2f1PU3AS1Kc +void f1(char __attribute__((address_space(1))) const *p) {} \ No newline at end of file diff --git a/test/CodeGenObjCXX/arc-attrs.mm b/test/CodeGenObjCXX/arc-attrs.mm index 0f0610f1721..d5716777193 100644 --- a/test/CodeGenObjCXX/arc-attrs.mm +++ b/test/CodeGenObjCXX/arc-attrs.mm @@ -12,7 +12,7 @@ void sanityTest() { id x = makeObject1(); // CHECK-NEXT: [[OBJ2:%.*]] = call i8* @_Z11makeObject2v() - // CHECK-NEXT: call void @_Z13releaseObjectP11objc_object(i8* [[OBJ2]]) + // CHECK-NEXT: call void @_Z13releaseObjectU11ns_consumedP11objc_object(i8* [[OBJ2]]) releaseObject(makeObject2()); // CHECK-NEXT: call void @objc_storeStrong(i8** [[X]], i8* null) @@ -31,16 +31,16 @@ void sanityTest() { // CHECK-LABEL: define void @_Z12templateTestv void templateTest() { // CHECK: [[X:%.*]] = alloca i8*, align 8 - // CHECK-NEXT: [[OBJ1:%.*]] = call i8* @_Z12makeObjectT1IU8__strongP11objc_objectET_v() + // CHECK-NEXT: [[OBJ1:%.*]] = call i8* @_Z12makeObjectT1IU8__strongP11objc_objectEU19ns_returns_retainedT_v() // CHECK-NEXT: store i8* [[OBJ1]], i8** [[X]], align 8 id x = makeObjectT1(); - // CHECK-NEXT: [[OBJ2:%.*]] = call i8* @_Z12makeObjectT2IU8__strongP11objc_objectET_v() - // CHECK-NEXT: call void @_Z13releaseObjectP11objc_object(i8* [[OBJ2]]) + // CHECK-NEXT: [[OBJ2:%.*]] = call i8* @_Z12makeObjectT2IU8__strongP11objc_objectEU19ns_returns_retainedT_v() + // CHECK-NEXT: call void @_Z13releaseObjectU11ns_consumedP11objc_object(i8* [[OBJ2]]) releaseObject(makeObjectT2()); // CHECK-NEXT: [[OBJ3:%.*]] = call i8* @_Z11makeObject1v() - // CHECK-NEXT: call void @_Z14releaseObjectTIU8__strongP11objc_objectEvT_(i8* [[OBJ3]]) + // CHECK-NEXT: call void @_Z14releaseObjectTIU8__strongP11objc_objectEvU11ns_consumedT_(i8* [[OBJ3]]) releaseObjectT(makeObject1()); // CHECK-NEXT: call void @objc_storeStrong(i8** [[X]], i8* null) diff --git a/test/CodeGenObjCXX/arc-mangle.mm b/test/CodeGenObjCXX/arc-mangle.mm index a168d41b336..84acbdb1448 100644 --- a/test/CodeGenObjCXX/arc-mangle.mm +++ b/test/CodeGenObjCXX/arc-mangle.mm @@ -8,15 +8,20 @@ void f(__weak id *) {} void f(__autoreleasing id *) {} // CHECK-LABEL: define {{.*}}void @_Z1fPP11objc_object(i8**) void f(__unsafe_unretained id *) {} -// CHECK-LABEL: define {{.*}}void @_Z1fPKU8__strongP11objc_object(i8**) +// CHECK-LABEL: define {{.*}}void @_Z1fPU8__strongKP11objc_object(i8**) void f(const __strong id *) {} -// CHECK-LABEL: define {{.*}}void @_Z1fPKU6__weakP11objc_object(i8**) +// CHECK-LABEL: define {{.*}}void @_Z1fPU6__weakKP11objc_object(i8**) void f(const __weak id *) {} -// CHECK-LABEL: define {{.*}}void @_Z1fPKU15__autoreleasingP11objc_object(i8**) +// CHECK-LABEL: define {{.*}}void @_Z1fPU15__autoreleasingKP11objc_object(i8**) void f(const __autoreleasing id *) {} // CHECK-LABEL: define {{.*}}void @_Z1fPKP11objc_object(i8**) void f(const __unsafe_unretained id *) {} - +// CHECK-LABEL: define {{.*}}void @_Z1fPFU19ns_returns_retainedP11objc_objectvE +void f(__attribute__((ns_returns_retained)) id (*fn)()) {} +// CHECK-LABEL: define {{.*}}void @_Z1fPFP11objc_objectU11ns_consumedS0_S0_E +void f(id (*fn)(__attribute__((ns_consumed)) id, id)) {} +// CHECK-LABEL: define {{.*}}void @_Z1fPFP11objc_objectS0_U11ns_consumedS0_E +void f(__strong id (*fn)(id, __attribute__((ns_consumed)) id)) {} template struct unsigned_c { }; diff --git a/test/CodeGenObjCXX/arc-move.mm b/test/CodeGenObjCXX/arc-move.mm index 76fb15b290d..d1710e291b0 100644 --- a/test/CodeGenObjCXX/arc-move.mm +++ b/test/CodeGenObjCXX/arc-move.mm @@ -72,10 +72,10 @@ void library_move(__strong id &y) { // CHECK-NEXT: ret void } -// CHECK-LABEL: define void @_Z10const_moveRKU8__strongP11objc_object( +// CHECK-LABEL: define void @_Z10const_moveRU8__strongKP11objc_object( void const_move(const __strong id &x) { // CHECK: [[Y:%.*]] = alloca i8*, - // CHECK: [[X:%.*]] = call dereferenceable({{[0-9]+}}) i8** @_Z4moveIRKU8__strongP11objc_objectEON16remove_referenceIT_E4typeEOS5_( + // CHECK: [[X:%.*]] = call dereferenceable({{[0-9]+}}) i8** @_Z4moveIRU8__strongKP11objc_objectEON16remove_referenceIT_E4typeEOS5_( // CHECK-NEXT: [[T0:%.*]] = load i8*, i8** [[X]] // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retain(i8* [[T0]]) // CHECK-NEXT: store i8* [[T1]], i8** [[Y]] From cfdb899df62436125ba76cb3e452909c638789eb Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 3 Mar 2016 00:10:03 +0000 Subject: [PATCH 460/742] Improve some infrastructure for extended parameter infos and fix a bug with the instantiation of ns_consumed parameter attributes in ARC. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262551 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 3 ++ include/clang/Sema/AttributeList.h | 35 ++++++++++---- include/clang/Sema/Sema.h | 4 ++ lib/AST/TypePrinter.cpp | 4 ++ lib/Sema/SemaDeclAttr.cpp | 56 +++++++++++++++------- lib/Sema/SemaTemplateInstantiateDecl.cpp | 8 ++++ lib/Sema/SemaType.cpp | 41 +++++++++++++--- test/SemaCXX/cxx11-gnu-attrs.cpp | 1 + test/SemaObjCXX/arc-nsconsumed-errors.mm | 4 +- test/SemaObjCXX/arc-templates.mm | 8 ++++ 10 files changed, 129 insertions(+), 35 deletions(-) diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 4794b4529a4..ae978a7e994 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -2929,6 +2929,9 @@ def warn_ns_attribute_wrong_return_type : Warning< "%0 attribute only applies to %select{functions|methods|properties}1 that " "return %select{an Objective-C object|a pointer|a non-retainable pointer}2">, InGroup; +def err_ns_attribute_wrong_parameter_type : Error< + "%0 attribute only applies to " + "%select{Objective-C object|pointer|pointer-to-CF-pointer}1 parameters">; def warn_ns_attribute_wrong_parameter_type : Warning< "%0 attribute only applies to " "%select{Objective-C object|pointer|pointer-to-CF-pointer}1 parameters">, diff --git a/include/clang/Sema/AttributeList.h b/include/clang/Sema/AttributeList.h index bf20271c877..7a161ba3951 100644 --- a/include/clang/Sema/AttributeList.h +++ b/include/clang/Sema/AttributeList.h @@ -116,9 +116,11 @@ class AttributeList { // TODO: This should really be called ParsedAttribute SourceLocation ScopeLoc; SourceLocation EllipsisLoc; + unsigned AttrKind : 16; + /// The number of expression arguments this attribute has. /// The expressions themselves are stored after the object. - unsigned NumArgs : 15; + unsigned NumArgs : 16; /// Corresponds to the Syntax enum. unsigned SyntaxUsed : 3; @@ -144,7 +146,11 @@ class AttributeList { // TODO: This should really be called ParsedAttribute /// True if this has a ParsedType unsigned HasParsedType : 1; - unsigned AttrKind : 8; + /// True if the processing cache is valid. + mutable unsigned HasProcessingCache : 1; + + /// A cached value. + mutable unsigned ProcessingCache : 8; /// \brief The location of the 'unavailable' keyword in an /// availability attribute. @@ -236,7 +242,8 @@ class AttributeList { // TODO: This should really be called ParsedAttribute ScopeLoc(scopeLoc), EllipsisLoc(ellipsisLoc), NumArgs(numArgs), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false), - HasParsedType(false), NextInPosition(nullptr), NextInPool(nullptr) { + HasParsedType(false), HasProcessingCache(false), + NextInPosition(nullptr), NextInPool(nullptr) { if (numArgs) memcpy(getArgsBuffer(), args, numArgs * sizeof(ArgsUnion)); AttrKind = getKind(getName(), getScopeName(), syntaxUsed); } @@ -255,8 +262,8 @@ class AttributeList { // TODO: This should really be called ParsedAttribute ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(1), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(true), IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false), - UnavailableLoc(unavailable), MessageExpr(messageExpr), - NextInPosition(nullptr), NextInPool(nullptr) { + HasProcessingCache(false), UnavailableLoc(unavailable), + MessageExpr(messageExpr), NextInPosition(nullptr), NextInPool(nullptr) { ArgsUnion PVal(Parm); memcpy(getArgsBuffer(), &PVal, sizeof(ArgsUnion)); new (getAvailabilityData()) AvailabilityData( @@ -275,7 +282,7 @@ class AttributeList { // TODO: This should really be called ParsedAttribute ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(3), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false), - NextInPosition(nullptr), NextInPool(nullptr) { + HasProcessingCache(false), NextInPosition(nullptr), NextInPool(nullptr) { ArgsVector Args; Args.push_back(Parm1); Args.push_back(Parm2); @@ -293,7 +300,7 @@ class AttributeList { // TODO: This should really be called ParsedAttribute ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(1), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(true), IsProperty(false), HasParsedType(false), - NextInPosition(nullptr), NextInPool(nullptr) { + HasProcessingCache(false), NextInPosition(nullptr), NextInPool(nullptr) { ArgsUnion PVal(ArgKind); memcpy(getArgsBuffer(), &PVal, sizeof(ArgsUnion)); TypeTagForDatatypeData &ExtraData = getTypeTagForDatatypeDataSlot(); @@ -311,7 +318,7 @@ class AttributeList { // TODO: This should really be called ParsedAttribute ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(0), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(true), - NextInPosition(nullptr), NextInPool(nullptr) { + HasProcessingCache(false), NextInPosition(nullptr), NextInPool(nullptr){ new (&getTypeBuffer()) ParsedType(typeArg); AttrKind = getKind(getName(), getScopeName(), syntaxUsed); } @@ -325,7 +332,7 @@ class AttributeList { // TODO: This should really be called ParsedAttribute ScopeLoc(scopeLoc), EllipsisLoc(), NumArgs(0), SyntaxUsed(syntaxUsed), Invalid(false), UsedAsTypeAttr(false), IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(true), HasParsedType(false), - NextInPosition(nullptr), NextInPool(nullptr) { + HasProcessingCache(false), NextInPosition(nullptr), NextInPool(nullptr) { new (&getPropertyDataBuffer()) PropertyData(getterId, setterId); AttrKind = getKind(getName(), getScopeName(), syntaxUsed); } @@ -377,6 +384,16 @@ class AttributeList { // TODO: This should really be called ParsedAttribute bool isInvalid() const { return Invalid; } void setInvalid(bool b = true) const { Invalid = b; } + bool hasProcessingCache() const { return HasProcessingCache; } + unsigned getProcessingCache() const { + assert(hasProcessingCache()); + return ProcessingCache; + } + void setProcessingCache(unsigned value) const { + ProcessingCache = value; + HasProcessingCache = true; + } + bool isUsedAsTypeAttr() const { return UsedAsTypeAttr; } void setUsedAsTypeAttr() { UsedAsTypeAttr = true; } diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 7958ff2743b..58d64441d56 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -7805,6 +7805,10 @@ class Sema { void AddLaunchBoundsAttr(SourceRange AttrRange, Decl *D, Expr *MaxThreads, Expr *MinBlocks, unsigned SpellingListIndex); + void AddNSConsumedAttr(SourceRange AttrRange, Decl *D, + unsigned SpellingListIndex, bool isNSConsumed, + bool isTemplateInstantiation); + //===--------------------------------------------------------------------===// // C++ Coroutines TS // diff --git a/lib/AST/TypePrinter.cpp b/lib/AST/TypePrinter.cpp index 762033b600e..3b96cc5f892 100644 --- a/lib/AST/TypePrinter.cpp +++ b/lib/AST/TypePrinter.cpp @@ -641,6 +641,10 @@ void TypePrinter::printFunctionProtoAfter(const FunctionProtoType *T, ParamPolicyRAII ParamPolicy(Policy); for (unsigned i = 0, e = T->getNumParams(); i != e; ++i) { if (i) OS << ", "; + + auto EPI = T->getExtParameterInfo(i); + if (EPI.isConsumed()) OS << "__attribute__((ns_consumed)) "; + print(T->getParamType(i), OS, StringRef()); } } diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 22a5fae572f..0174a1dd814 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -3795,6 +3795,11 @@ bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC, if (attr.isInvalid()) return true; + if (attr.hasProcessingCache()) { + CC = (CallingConv) attr.getProcessingCache(); + return false; + } + unsigned ReqArgs = attr.getKind() == AttributeList::AT_Pcs ? 1 : 0; if (!checkAttributeNumArgs(*this, attr, ReqArgs)) { attr.setInvalid(); @@ -3856,6 +3861,7 @@ bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC, CC = TI.getDefaultCallingConv(MT); } + attr.setProcessingCache((unsigned) CC); return false; } @@ -4050,31 +4056,45 @@ static bool isValidSubjectOfCFAttribute(Sema &S, QualType type) { } static void handleNSConsumedAttr(Sema &S, Decl *D, const AttributeList &Attr) { + S.AddNSConsumedAttr(Attr.getRange(), D, Attr.getAttributeSpellingListIndex(), + Attr.getKind() == AttributeList::AT_NSConsumed, + /*template instantiation*/ false); +} + +void Sema::AddNSConsumedAttr(SourceRange attrRange, Decl *D, + unsigned spellingIndex, bool isNSConsumed, + bool isTemplateInstantiation) { ParmVarDecl *param = cast(D); - bool typeOK, cf; + bool typeOK; - if (Attr.getKind() == AttributeList::AT_NSConsumed) { - typeOK = isValidSubjectOfNSAttribute(S, param->getType()); - cf = false; + if (isNSConsumed) { + typeOK = isValidSubjectOfNSAttribute(*this, param->getType()); } else { - typeOK = isValidSubjectOfCFAttribute(S, param->getType()); - cf = true; + typeOK = isValidSubjectOfCFAttribute(*this, param->getType()); } if (!typeOK) { - S.Diag(D->getLocStart(), diag::warn_ns_attribute_wrong_parameter_type) - << Attr.getRange() << Attr.getName() << cf; - return; - } - - if (cf) - param->addAttr(::new (S.Context) - CFConsumedAttr(Attr.getRange(), S.Context, - Attr.getAttributeSpellingListIndex())); + // These attributes are normally just advisory, but in ARC, ns_consumed + // is significant. Allow non-dependent code to contain inappropriate + // attributes even in ARC, but require template instantiations to be + // set up correctly. + Diag(D->getLocStart(), + (isTemplateInstantiation && isNSConsumed && + getLangOpts().ObjCAutoRefCount + ? diag::err_ns_attribute_wrong_parameter_type + : diag::warn_ns_attribute_wrong_parameter_type)) + << attrRange + << (isNSConsumed ? "ns_consumed" : "cf_consumed") + << (isNSConsumed ? /*objc pointers*/ 0 : /*cf pointers*/ 1); + return; + } + + if (isNSConsumed) + param->addAttr(::new (Context) + NSConsumedAttr(attrRange, Context, spellingIndex)); else - param->addAttr(::new (S.Context) - NSConsumedAttr(Attr.getRange(), S.Context, - Attr.getAttributeSpellingListIndex())); + param->addAttr(::new (Context) + CFConsumedAttr(attrRange, Context, spellingIndex)); } static void handleNSReturnsRetainedAttr(Sema &S, Decl *D, diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 854c6ff9482..2be79da6da5 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -273,6 +273,14 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs, } } + if (isa(TmplAttr) || isa(TmplAttr)) { + AddNSConsumedAttr(TmplAttr->getRange(), New, + TmplAttr->getSpellingListIndex(), + isa(TmplAttr), + /*template instantiation*/ true); + continue; + } + assert(!TmplAttr->isPackExpansion()); if (TmplAttr->isLateParsed() && LateAttrs) { // Late parsed attributes must be instantiated and attached after the diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 784fe945f5b..a77edf4f762 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -100,9 +100,8 @@ static void diagnoseBadTypeAttribute(Sema &S, const AttributeList &attr, case AttributeList::AT_ObjCGC: \ case AttributeList::AT_ObjCOwnership -// Function type attributes. -#define FUNCTION_TYPE_ATTRS_CASELIST \ - case AttributeList::AT_NoReturn: \ +// Calling convention attributes. +#define CALLING_CONV_ATTRS_CASELIST \ case AttributeList::AT_CDecl: \ case AttributeList::AT_FastCall: \ case AttributeList::AT_StdCall: \ @@ -111,12 +110,17 @@ static void diagnoseBadTypeAttribute(Sema &S, const AttributeList &attr, case AttributeList::AT_VectorCall: \ case AttributeList::AT_MSABI: \ case AttributeList::AT_SysVABI: \ - case AttributeList::AT_Regparm: \ case AttributeList::AT_Pcs: \ case AttributeList::AT_IntelOclBicc: \ case AttributeList::AT_PreserveMost: \ case AttributeList::AT_PreserveAll +// Function type attributes. +#define FUNCTION_TYPE_ATTRS_CASELIST \ + case AttributeList::AT_NoReturn: \ + case AttributeList::AT_Regparm: \ + CALLING_CONV_ATTRS_CASELIST + // Microsoft-specific type qualifiers. #define MS_TYPE_ATTRS_CASELIST \ case AttributeList::AT_Ptr32: \ @@ -2948,6 +2952,26 @@ getCCForDeclaratorChunk(Sema &S, Declarator &D, unsigned ChunkIndex) { assert(D.getTypeObject(ChunkIndex).Kind == DeclaratorChunk::Function); + // Check for an explicit CC attribute. + for (auto Attr = FTI.AttrList; Attr; Attr = Attr->getNext()) { + switch (Attr->getKind()) { + CALLING_CONV_ATTRS_CASELIST: { + // Ignore attributes that don't validate or can't apply to the + // function type. We'll diagnose the failure to apply them in + // handleFunctionTypeAttr. + CallingConv CC; + if (!S.CheckCallingConvAttr(*Attr, CC) && + (!FTI.isVariadic || supportsVariadicCall(CC))) { + return CC; + } + break; + } + + default: + break; + } + } + bool IsCXXInstanceMethod = false; if (S.getLangOpts().CPlusPlus) { @@ -5994,9 +6018,14 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state, // Modify the CC from the wrapped function type, wrap it all back, and then // wrap the whole thing in an AttributedType as written. The modified type // might have a different CC if we ignored the attribute. - FunctionType::ExtInfo EI = unwrapped.get()->getExtInfo().withCallingConv(CC); - QualType Equivalent = + QualType Equivalent; + if (CCOld == CC) { + Equivalent = type; + } else { + auto EI = unwrapped.get()->getExtInfo().withCallingConv(CC); + Equivalent = unwrapped.wrap(S, S.Context.adjustFunctionType(unwrapped.get(), EI)); + } type = S.Context.getAttributedType(CCAttrKind, type, Equivalent); return true; } diff --git a/test/SemaCXX/cxx11-gnu-attrs.cpp b/test/SemaCXX/cxx11-gnu-attrs.cpp index d20617815e6..231be727714 100644 --- a/test/SemaCXX/cxx11-gnu-attrs.cpp +++ b/test/SemaCXX/cxx11-gnu-attrs.cpp @@ -19,6 +19,7 @@ int *[[gnu::unused]] attr_on_ptr; [[gnu::fastcall]] void pr17424_4() [[gnu::stdcall]]; // expected-warning@-1 {{calling convention 'fastcall' ignored for this target}} // expected-warning@-2 {{attribute 'stdcall' ignored, because it cannot be applied to a type}} +// expected-warning@-3 {{calling convention 'stdcall' ignored for this target}} void pr17424_5 [[gnu::fastcall]](); // expected-warning@-1 {{calling convention 'fastcall' ignored for this target}} diff --git a/test/SemaObjCXX/arc-nsconsumed-errors.mm b/test/SemaObjCXX/arc-nsconsumed-errors.mm index c78d8a5f4ad..638a1ebd2ad 100644 --- a/test/SemaObjCXX/arc-nsconsumed-errors.mm +++ b/test/SemaObjCXX/arc-nsconsumed-errors.mm @@ -29,9 +29,9 @@ releaser_t r2 = releaser; // no-warning template -void templateFunction(T) { } // expected-note {{candidate template ignored: could not match 'void (__strong id)' against 'void (id)'}} \ +void templateFunction(T) { } // expected-note {{candidate template ignored: could not match 'void (__strong id)' against 'void (__attribute__((ns_consumed)) id)'}} \ // expected-note {{candidate template ignored: failed template argument deduction}} -releaser_t r3 = templateFunction; // expected-error {{address of overloaded function 'templateFunction' does not match required type 'void (id)'}} +releaser_t r3 = templateFunction; // expected-error {{address of overloaded function 'templateFunction' does not match required type 'void (__attribute__((ns_consumed)) id)'}} template void templateReleaser(__attribute__((ns_consumed)) T) { } // expected-note 2{{candidate template ignored: failed template argument deduction}} diff --git a/test/SemaObjCXX/arc-templates.mm b/test/SemaObjCXX/arc-templates.mm index 81425985e62..97854dff8c1 100644 --- a/test/SemaObjCXX/arc-templates.mm +++ b/test/SemaObjCXX/arc-templates.mm @@ -442,3 +442,11 @@ void test2() { take_no(produces<4>::fn); // expected-error {{no matching function}} } } + +namespace instantiate_consumed { + template void take(CONSUMED T t) {} // expected-note {{candidate template ignored: substitution failure [with T = int]: ns_consumed attribute only applies to Objective-C object parameters}} + void test() { + take((id) 0); + take((int) 0); // expected-error {{no matching function for call to 'take'}} + } +} From c93f84bf1d9899c160f35001348db11355ac0953 Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 4 Apr 2016 11:59:39 -0700 Subject: [PATCH 461/742] Manglings for Roman's new calling conventions. --- lib/AST/ItaniumMangle.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index 269278b96dd..c5b6a358939 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -2151,6 +2151,11 @@ StringRef CXXNameMangler::getCallingConvQualifierName(CallingConv CC) { case CC_C: return ""; + case CC_PreserveMost: + return "perservemost"; + case CC_PreserveAll: + return "perserveall"; + case CC_X86StdCall: case CC_X86FastCall: case CC_X86ThisCall: From 88547d6b0837f8e376ed434fe65d2b09caaabb5a Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 3 Mar 2016 06:39:32 +0000 Subject: [PATCH 462/742] Semantic analysis for the swiftcall calling convention. I've tried to keep the infrastructure behind parameter ABI treatments fairly general. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262587 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang-c/Index.h | 1 + include/clang/AST/Attr.h | 30 +++++ include/clang/AST/Type.h | 21 ++- include/clang/Basic/Attr.td | 26 ++++ include/clang/Basic/AttrDocs.td | 143 +++++++++++++++++++++ include/clang/Basic/DiagnosticSemaKinds.td | 14 ++ include/clang/Basic/Specifiers.h | 24 ++++ include/clang/Sema/Sema.h | 3 + lib/AST/ItaniumMangle.cpp | 17 ++- lib/AST/Type.cpp | 3 + lib/AST/TypePrinter.cpp | 21 +++ lib/Basic/Targets.cpp | 33 +++-- lib/Sema/SemaDeclAttr.cpp | 106 +++++++++++++++ lib/Sema/SemaTemplateInstantiateDecl.cpp | 6 + lib/Sema/SemaType.cpp | 89 ++++++++++++- test/Sema/attr-swiftcall.c | 30 +++++ test/SemaCXX/attr-swiftcall.cpp | 37 ++++++ tools/libclang/CXType.cpp | 1 + utils/TableGen/ClangAttrEmitter.cpp | 3 +- 19 files changed, 595 insertions(+), 13 deletions(-) create mode 100644 test/Sema/attr-swiftcall.c create mode 100644 test/SemaCXX/attr-swiftcall.cpp diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h index cbb5052ecfc..28ec30ccba5 100644 --- a/include/clang-c/Index.h +++ b/include/clang-c/Index.h @@ -2943,6 +2943,7 @@ enum CXCallingConv { CXCallingConv_X86_64Win64 = 10, CXCallingConv_X86_64SysV = 11, CXCallingConv_X86VectorCall = 12, + CXCallingConv_Swift = 13, CXCallingConv_PreserveMost = 14, CXCallingConv_PreserveAll = 15, diff --git a/include/clang/AST/Attr.h b/include/clang/AST/Attr.h index 698909f0335..4d864edf6a5 100644 --- a/include/clang/AST/Attr.h +++ b/include/clang/AST/Attr.h @@ -149,6 +149,36 @@ class InheritableParamAttr : public InheritableAttr { } }; +/// A parameter attribute which changes the argument-passing ABI rule +/// for the parameter. +class ParameterABIAttr : public InheritableParamAttr { +protected: + ParameterABIAttr(attr::Kind AK, SourceRange R, + unsigned SpellingListIndex, bool IsLateParsed, + bool DuplicatesAllowed) + : InheritableParamAttr(AK, R, SpellingListIndex, IsLateParsed, + DuplicatesAllowed) {} + +public: + ParameterABI getABI() const { + switch (getKind()) { + case attr::SwiftContext: + return ParameterABI::SwiftContext; + case attr::SwiftErrorResult: + return ParameterABI::SwiftErrorResult; + case attr::SwiftIndirectResult: + return ParameterABI::SwiftIndirectResult; + default: + llvm_unreachable("bad parameter ABI attribute kind"); + } + } + + static bool classof(const Attr *A) { + return A->getKind() >= attr::FirstParameterABIAttr && + A->getKind() <= attr::LastParameterABIAttr; + } +}; + #include "clang/AST/Attrs.inc" inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB, diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 4e26236024b..2e02ad1ea0f 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -3061,12 +3061,23 @@ class FunctionProtoType : public FunctionType, public llvm::FoldingSetNode { /// not produce the latter. class ExtParameterInfo { enum { - IsConsumed = 0x01, + ABIMask = 0x0F, + IsConsumed = 0x10 }; unsigned char Data; public: ExtParameterInfo() : Data(0) {} + /// Return the ABI treatment of this parameter. + ParameterABI getABI() const { + return ParameterABI(Data & ABIMask); + } + ExtParameterInfo withABI(ParameterABI kind) const { + ExtParameterInfo copy = *this; + copy.Data = (copy.Data & ~ABIMask) | unsigned(kind); + return copy; + } + /// Is this parameter considered "consumed" by Objective-C ARC? /// Consumed parameters must have retainable object type. bool isConsumed() const { @@ -3392,6 +3403,13 @@ class FunctionProtoType : public FunctionType, public llvm::FoldingSetNode { return ExtParameterInfo(); } + ParameterABI getParameterABI(unsigned I) const { + assert(I < getNumParams() && "parameter index out of range"); + if (hasExtParameterInfos()) + return getExtParameterInfosBuffer()[I].getABI(); + return ParameterABI::Ordinary; + } + bool isParamConsumed(unsigned I) const { assert(I < getNumParams() && "parameter index out of range"); if (hasExtParameterInfos()) @@ -3717,6 +3735,7 @@ class AttributedType : public Type, public llvm::FoldingSetNode { attr_stdcall, attr_thiscall, attr_pascal, + attr_swiftcall, attr_vectorcall, attr_inteloclbicc, attr_ms_abi, diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 06da89888ab..1861998bc4d 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -341,6 +341,11 @@ class TargetSpecificAttr { /// redeclarations, even when it's written on a parameter. class InheritableParamAttr : InheritableAttr; +/// An attribute which changes the ABI rules for a specific parameter. +class ParameterABIAttr : InheritableParamAttr { + let Subjects = SubjectList<[ParmVar]>; +} + /// An ignored attribute, which we parse but discard with no checking. class IgnoredAttr : Attr { let Ignored = 1; @@ -1406,6 +1411,27 @@ def StdCall : InheritableAttr { let Documentation = [StdCallDocs]; } +def SwiftCall : InheritableAttr { + let Spellings = [GCC<"swiftcall">]; +// let Subjects = SubjectList<[Function]>; + let Documentation = [SwiftCallDocs]; +} + +def SwiftContext : ParameterABIAttr { + let Spellings = [GCC<"swift_context">]; + let Documentation = [SwiftContextDocs]; +} + +def SwiftErrorResult : ParameterABIAttr { + let Spellings = [GCC<"swift_error_result">]; + let Documentation = [SwiftErrorResultDocs]; +} + +def SwiftIndirectResult : ParameterABIAttr { + let Spellings = [GCC<"swift_indirect_result">]; + let Documentation = [SwiftIndirectResultDocs]; +} + def SysVABI : InheritableAttr { let Spellings = [GCC<"sysv_abi">]; // let Subjects = [Function, ObjCMethod]; diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td index a192c8a4a08..008b10d6030 100644 --- a/include/clang/Basic/AttrDocs.td +++ b/include/clang/Basic/AttrDocs.td @@ -2061,3 +2061,146 @@ experimental at this time. }]; } +def SwiftCallDocs : Documentation { + let Category = DocCatVariable; + let Content = [{ +The ``swiftcall`` attribute indicates that a function should be called +using the Swift calling convention for a function or function pointer. + +The lowering for the Swift calling convention, as described by the Swift +ABI documentation, occurs in multiple phases. The first, "high-level" +phase breaks down the formal parameters and results into innately direct +and indirect components, adds implicit paraameters for the generic +signature, and assigns the context and error ABI treatments to parameters +where applicable. The second phase breaks down the direct parameters +and results from the first phase and assigns them to registers or the +stack. The ``swiftcall`` convention only handles this second phase of +lowering; the C function type must accurately reflect the results +of the first phase, as follows: + +- Results classified as indirect by high-level lowering should be + represented as parameters with the ``swift_indirect_result`` attribute. + +- Results classified as direct by high-level lowering should be represented + as follows: + + - First, remove any empty direct results. + + - If there are no direct results, the C result type should be ``void``. + + - If there is one direct result, the C result type should be a type with + the exact layout of that result type. + + - If there are a multiple direct results, the C result type should be + a struct type with the exact layout of a tuple of those results. + +- Parameters classified as indirect by high-level lowering should be + represented as parameters of pointer type. + +- Parameters classified as direct by high-level lowering should be + omitted if they are empty types; otherwise, they should be represented + as a parameter type with a layout exactly matching the layout of the + Swift parameter type. + +- The context parameter, if present, should be represented as a trailing + parameter with the ``swift_context`` attribute. + +- The error result parameter, if present, should be represented as a + trailing parameter (always following a context parameter) with the + ``swift_error_result`` attribute. + +``swiftcall`` does not support variadic arguments or unprototyped functions. + +The parameter ABI treatment attributes are aspects of the function type. +A function type which which applies an ABI treatment attribute to a +parameter is a different type from an otherwise-identical function type +that does not. A single parameter may not have multiple ABI treatment +attributes. + +Support for this feature is target-dependent, although it should be +supported on every target that Swift supports. Query for this support +with ``__has_attribute(swiftcall)``. This implies support for the +``swift_context``, ``swift_error_result``, and ``swift_indirect_result`` +attributes. + }]; +} + +def SwiftContextDocs : Documentation { + let Category = DocCatVariable; + let Content = [{ +The ``swift_context`` attribute marks a parameter of a ``swiftcall`` +function as having the special context-parameter ABI treatment. + +This treatment generally passes the context value in a special register +which is normally callee-preserved. + +A ``swift_context`` parameter must either be the last parameter or must be +followed by a ``swift_error_result`` parameter (which itself must always be +the last parameter). + +A context parameter must have pointer or reference type. + }]; +} + +def SwiftErrorResultDocs : Documentation { + let Category = DocCatVariable; + let Content = [{ +The ``swift_error_result`` attribute marks a parameter of a ``swiftcall`` +function as having the special error-result ABI treatment. + +This treatment generally passes the underlying error value in and out of +the function through a special register which is normally callee-preserved. +This is modeled in C by pretending that the register is addressable memory: + +- The caller appears to pass the address of a variable of pointer type. + The current value of this variable is copied into the register before + the call; if the call returns normally, the value is copied back into the + variable. + +- The callee appears to receive the address of a variable. This address + is actually a hidden location in its own stack, initialized with the + value of the register upon entry. When the function returns normally, + the value in that hidden location is written back to the register. + +A ``swift_error_result`` parameter must be the last parameter, and it must be +preceded by a ``swift_context`` parameter. + +A ``swift_error_result`` parameter must have type ``T**`` or ``T*&`` for some +type T. Note that no qualifiers are permitted on the intermediate level. + +It is undefined behavior if the caller does not pass a pointer or +reference to a valid object. + +The standard convention is that the error value itself (that is, the +value stored in the apparent argument) will be null upon function entry, +but this is not enforced by the ABI. + }]; +} + +def SwiftIndirectResultDocs : Documentation { + let Category = DocCatVariable; + let Content = [{ +The ``swift_indirect_result`` attribute marks a parameter of a ``swiftcall`` +function as having the special indirect-result ABI treatmenet. + +This treatment gives the parameter the target's normal indirect-result +ABI treatment, which may involve passing it differently from an ordinary +parameter. However, only the first indirect result will receive this +treatment. Furthermore, low-level lowering may decide that a direct result +must be returned indirectly; if so, this will take priority over the +``swift_indirect_result`` parameters. + +A ``swift_indirect_result`` parameter must either be the first parameter or +follow another ``swift_indirect_result`` parameter. + +A ``swift_indirect_result`` parameter must have type ``T*`` or ``T&`` for +some object type ``T``. If ``T`` is a complete type at the point of +definition of a function, it is undefined behavior if the argument +value does not point to storage of adequate size and alignment for a +value of type ``T``. + +Making indirect results explicit in the signature allows C functions to +directly construct objects into them without relying on language +optimizations like C++'s named return value optimization (NRVO). + }]; +} diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index ae978a7e994..e8cbf79e1bd 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -2267,6 +2267,20 @@ def warn_objc_collection_literal_element : Warning< "object of type %0 is not compatible with " "%select{array element type|dictionary key type|dictionary value type}1 %2">, InGroup; +def err_swift_param_attr_not_swiftcall : Error< + "'%0' parameter can only be used with swiftcall calling convention">; +def err_swift_indirect_result_not_first : Error< + "'swift_indirect_result' parameters must be first parameters of function">; +def err_swift_context_not_before_swift_error_result : Error< + "'swift_context' parameter can only be followed by 'swift_error_result' " + "parameter">; +def err_swift_error_result_not_last : Error< + "'swift_error_result' parameter must be last parameter of function">; +def err_swift_error_result_not_after_swift_context : Error< + "'swift_error_result' parameter must follow 'swift_context' parameter">; +def err_swift_abi_parameter_wrong_type : Error< + "'%0' parameter must have pointer%select{| to unqualified pointer}1 type; " + "type here is %2">; def err_attribute_argument_is_zero : Error< "%0 attribute must be greater than 0">; diff --git a/include/clang/Basic/Specifiers.h b/include/clang/Basic/Specifiers.h index c9529eb223f..8857e0df565 100644 --- a/include/clang/Basic/Specifiers.h +++ b/include/clang/Basic/Specifiers.h @@ -239,6 +239,7 @@ namespace clang { CC_IntelOclBicc, // __attribute__((intel_ocl_bicc)) CC_SpirFunction, // default for OpenCL functions on SPIR target CC_SpirKernel, // inferred for OpenCL kernels on SPIR target + CC_Swift, // __attribute__((swiftcall)) CC_PreserveMost, // __attribute__((preserve_most)) CC_PreserveAll, // __attribute__((preserve_all)) }; @@ -254,6 +255,7 @@ namespace clang { case CC_X86VectorCall: case CC_SpirFunction: case CC_SpirKernel: + case CC_Swift: return false; default: return true; @@ -285,6 +287,28 @@ namespace clang { /// Retrieve the spelling of the given nullability kind. llvm::StringRef getNullabilitySpelling(NullabilityKind kind, bool isContextSensitive = false); + + /// \brief Kinds of parameter ABI. + enum class ParameterABI { + /// This parameter uses ordinary ABI rules for its type. + Ordinary, + + /// This parameter (which must have pointer type) is a Swift + /// indirect result parameter. + SwiftIndirectResult, + + /// This parameter (which must have pointer-to-pointer type) uses + /// the special Swift error-result ABI treatment. There can be at + /// most one parameter on a given function that uses this treatment. + SwiftErrorResult, + + /// This parameter (which must have pointer type) uses the special + /// Swift context-pointer ABI treatment. There can be at + /// most one parameter on a given function that uses this treatment. + SwiftContext + }; + + llvm::StringRef getParameterABISpelling(ParameterABI kind); } // end namespace clang #endif // LLVM_CLANG_BASIC_SPECIFIERS_H diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 58d64441d56..237efafa21a 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -7805,6 +7805,9 @@ class Sema { void AddLaunchBoundsAttr(SourceRange AttrRange, Decl *D, Expr *MaxThreads, Expr *MinBlocks, unsigned SpellingListIndex); + void AddParameterABIAttr(SourceRange AttrRange, Decl *D, + ParameterABI ABI, unsigned SpellingListIndex); + void AddNSConsumedAttr(SourceRange AttrRange, Decl *D, unsigned SpellingListIndex, bool isNSConsumed, bool isTemplateInstantiation); diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index c5b6a358939..b8022a4b56d 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -2170,6 +2170,9 @@ StringRef CXXNameMangler::getCallingConvQualifierName(CallingConv CC) { case CC_SpirKernel: // FIXME: we should be mangling all of the above. return ""; + + case CC_Swift: + return "swiftcall"; } llvm_unreachable("bad calling convention"); } @@ -2198,8 +2201,20 @@ CXXNameMangler::mangleExtParameterInfo(FunctionProtoType::ExtParameterInfo PI) { // Note that these are *not* substitution candidates. Demanglers might // have trouble with this if the parameter type is fully substituted. + switch (PI.getABI()) { + case ParameterABI::Ordinary: + break; + + // All of these start with "swift", so they come before "ns_consumed". + case ParameterABI::SwiftContext: + case ParameterABI::SwiftErrorResult: + case ParameterABI::SwiftIndirectResult: + mangleVendorQualifier(getParameterABISpelling(PI.getABI())); + break; + } + if (PI.isConsumed()) - Out << "U11ns_consumed"; + mangleVendorQualifier("ns_consumed"); } // ::= diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index a1b457b51a2..74aa323dbc8 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -2655,6 +2655,7 @@ StringRef FunctionType::getNameForCallConv(CallingConv CC) { case CC_IntelOclBicc: return "intel_ocl_bicc"; case CC_SpirFunction: return "spir_function"; case CC_SpirKernel: return "spir_kernel"; + case CC_Swift: return "swiftcall"; case CC_PreserveMost: return "preserve_most"; case CC_PreserveAll: return "preserve_all"; } @@ -2997,6 +2998,7 @@ bool AttributedType::isQualifier() const { case AttributedType::attr_stdcall: case AttributedType::attr_thiscall: case AttributedType::attr_pascal: + case AttributedType::attr_swiftcall: case AttributedType::attr_vectorcall: case AttributedType::attr_inteloclbicc: case AttributedType::attr_preserve_most: @@ -3052,6 +3054,7 @@ bool AttributedType::isCallingConv() const { case attr_fastcall: case attr_stdcall: case attr_thiscall: + case attr_swiftcall: case attr_vectorcall: case attr_pascal: case attr_ms_abi: diff --git a/lib/AST/TypePrinter.cpp b/lib/AST/TypePrinter.cpp index 3b96cc5f892..7c519fc6381 100644 --- a/lib/AST/TypePrinter.cpp +++ b/lib/AST/TypePrinter.cpp @@ -629,6 +629,20 @@ void TypePrinter::printFunctionProtoBefore(const FunctionProtoType *T, } } +llvm::StringRef clang::getParameterABISpelling(ParameterABI ABI) { + switch (ABI) { + case ParameterABI::Ordinary: + llvm_unreachable("asking for spelling of ordinary parameter ABI"); + case ParameterABI::SwiftContext: + return "swift_context"; + case ParameterABI::SwiftErrorResult: + return "swift_error_result"; + case ParameterABI::SwiftIndirectResult: + return "swift_indirect_result"; + } + llvm_unreachable("bad parameter ABI kind"); +} + void TypePrinter::printFunctionProtoAfter(const FunctionProtoType *T, raw_ostream &OS) { // If needed for precedence reasons, wrap the inner part in grouping parens. @@ -644,6 +658,9 @@ void TypePrinter::printFunctionProtoAfter(const FunctionProtoType *T, auto EPI = T->getExtParameterInfo(i); if (EPI.isConsumed()) OS << "__attribute__((ns_consumed)) "; + auto ABI = EPI.getABI(); + if (ABI != ParameterABI::Ordinary) + OS << "__attribute__((" << getParameterABISpelling(ABI) << ")) "; print(T->getParamType(i), OS, StringRef()); } @@ -707,6 +724,9 @@ void TypePrinter::printFunctionProtoAfter(const FunctionProtoType *T, case CC_SpirKernel: // Do nothing. These CCs are not available as attributes. break; + case CC_Swift: + OS << " __attribute__((swiftcall))"; + break; case CC_PreserveMost: OS << " __attribute__((preserve_most))"; break; @@ -1315,6 +1335,7 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, case AttributedType::attr_fastcall: OS << "fastcall"; break; case AttributedType::attr_stdcall: OS << "stdcall"; break; case AttributedType::attr_thiscall: OS << "thiscall"; break; + case AttributedType::attr_swiftcall: OS << "swiftcall"; break; case AttributedType::attr_vectorcall: OS << "vectorcall"; break; case AttributedType::attr_pascal: OS << "pascal"; break; case AttributedType::attr_ms_abi: OS << "ms_abi"; break; diff --git a/lib/Basic/Targets.cpp b/lib/Basic/Targets.cpp index 8920589eb56..7767ad5e0d1 100644 --- a/lib/Basic/Targets.cpp +++ b/lib/Basic/Targets.cpp @@ -2528,14 +2528,20 @@ class X86TargetInfo : public TargetInfo { bool setFPMath(StringRef Name) override; CallingConvCheckResult checkCallingConvention(CallingConv CC) const override { - // We accept all non-ARM calling conventions - return (CC == CC_X86ThisCall || - CC == CC_X86FastCall || - CC == CC_X86StdCall || - CC == CC_X86VectorCall || - CC == CC_C || - CC == CC_X86Pascal || - CC == CC_IntelOclBicc) ? CCCR_OK : CCCR_Warning; + // Most of the non-ARM calling conventions are i386 conventions. + switch (CC) { + case CC_X86ThisCall: + case CC_X86FastCall: + case CC_X86StdCall: + case CC_X86VectorCall: + case CC_C: + case CC_Swift: + case CC_X86Pascal: + case CC_IntelOclBicc: + return CCCR_OK; + default: + return CCCR_Warning; + } } CallingConv getDefaultCallingConv(CallingConvMethodType MT) const override { @@ -4026,6 +4032,7 @@ class X86_64TargetInfo : public X86TargetInfo { CallingConvCheckResult checkCallingConvention(CallingConv CC) const override { switch (CC) { case CC_C: + case CC_Swift: case CC_X86VectorCall: case CC_IntelOclBicc: case CC_X86_64Win64: @@ -5010,7 +5017,14 @@ class ARMTargetInfo : public TargetInfo { } CallingConvCheckResult checkCallingConvention(CallingConv CC) const override { - return (CC == CC_AAPCS || CC == CC_AAPCS_VFP) ? CCCR_OK : CCCR_Warning; + switch (CC) { + case CC_AAPCS: + case CC_AAPCS_VFP: + case CC_Swift: + return CCCR_OK; + default: + return CCCR_Warning; + } } int getEHDataRegisterNumber(unsigned RegNo) const override { @@ -5465,6 +5479,7 @@ class AArch64TargetInfo : public TargetInfo { CallingConvCheckResult checkCallingConvention(CallingConv CC) const override { switch (CC) { case CC_C: + case CC_Swift: case CC_PreserveMost: case CC_PreserveAll: return CCCR_OK; diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 0174a1dd814..3ecfd989038 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -3739,6 +3739,11 @@ static void handleCallConvAttr(Sema &S, Decl *D, const AttributeList &Attr) { PascalAttr(Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); return; + case AttributeList::AT_SwiftCall: + D->addAttr(::new (S.Context) + SwiftCallAttr(Attr.getRange(), S.Context, + Attr.getAttributeSpellingListIndex())); + return; case AttributeList::AT_VectorCall: D->addAttr(::new (S.Context) VectorCallAttr(Attr.getRange(), S.Context, @@ -3813,6 +3818,7 @@ bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC, case AttributeList::AT_StdCall: CC = CC_X86StdCall; break; case AttributeList::AT_ThisCall: CC = CC_X86ThisCall; break; case AttributeList::AT_Pascal: CC = CC_X86Pascal; break; + case AttributeList::AT_SwiftCall: CC = CC_Swift; break; case AttributeList::AT_VectorCall: CC = CC_X86VectorCall; break; case AttributeList::AT_MSABI: CC = Context.getTargetInfo().getTriple().isOSWindows() ? CC_C : @@ -3865,6 +3871,96 @@ bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC, return false; } +/// Pointer-like types in the default address space. +static bool isValidSwiftContextType(QualType type) { + if (!type->hasPointerRepresentation()) + return type->isDependentType(); + return type->getPointeeType().getAddressSpace() == 0; +} + +/// Pointers and references in the default address space. +static bool isValidSwiftIndirectResultType(QualType type) { + if (auto ptrType = type->getAs()) { + type = ptrType->getPointeeType(); + } else if (auto refType = type->getAs()) { + type = refType->getPointeeType(); + } else { + return type->isDependentType(); + } + return type.getAddressSpace() == 0; +} + +/// Pointers and references to pointers in the default address space. +static bool isValidSwiftErrorResultType(QualType type) { + if (auto ptrType = type->getAs()) { + type = ptrType->getPointeeType(); + } else if (auto refType = type->getAs()) { + type = refType->getPointeeType(); + } else { + return type->isDependentType(); + } + if (!type.getQualifiers().empty()) + return false; + return isValidSwiftContextType(type); +} + +static void handleParameterABIAttr(Sema &S, Decl *D, const AttributeList &attr, + ParameterABI abi) { + S.AddParameterABIAttr(attr.getRange(), D, abi, + attr.getAttributeSpellingListIndex()); +} + +void Sema::AddParameterABIAttr(SourceRange range, Decl *D, ParameterABI abi, + unsigned spellingIndex) { + + QualType type = cast(D)->getType(); + + if (auto existingAttr = D->getAttr()) { + if (existingAttr->getABI() != abi) { + Diag(range.getBegin(), diag::err_attributes_are_not_compatible) + << getParameterABISpelling(abi) << existingAttr; + Diag(existingAttr->getLocation(), diag::note_conflicting_attribute); + return; + } + } + + switch (abi) { + case ParameterABI::Ordinary: + llvm_unreachable("explicit attribute for ordinary parameter ABI?"); + + case ParameterABI::SwiftContext: + if (!isValidSwiftContextType(type)) { + Diag(range.getBegin(), diag::err_swift_abi_parameter_wrong_type) + << getParameterABISpelling(abi) + << /*pointer to pointer */ 0 << type; + } + D->addAttr(::new (Context) + SwiftContextAttr(range, Context, spellingIndex)); + return; + + case ParameterABI::SwiftErrorResult: + if (!isValidSwiftErrorResultType(type)) { + Diag(range.getBegin(), diag::err_swift_abi_parameter_wrong_type) + << getParameterABISpelling(abi) + << /*pointer to pointer */ 1 << type; + } + D->addAttr(::new (Context) + SwiftErrorResultAttr(range, Context, spellingIndex)); + return; + + case ParameterABI::SwiftIndirectResult: + if (!isValidSwiftIndirectResultType(type)) { + Diag(range.getBegin(), diag::err_swift_abi_parameter_wrong_type) + << getParameterABISpelling(abi) + << /*pointer*/ 0 << type; + } + D->addAttr(::new (Context) + SwiftIndirectResultAttr(range, Context, spellingIndex)); + return; + } + llvm_unreachable("bad parameter ABI attribute"); +} + /// Checks a regparm attribute, returning true if it is ill-formed and /// otherwise setting numParams to the appropriate value. bool Sema::CheckRegparmAttr(const AttributeList &Attr, unsigned &numParams) { @@ -5842,6 +5938,7 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_FastCall: case AttributeList::AT_ThisCall: case AttributeList::AT_Pascal: + case AttributeList::AT_SwiftCall: case AttributeList::AT_VectorCall: case AttributeList::AT_MSABI: case AttributeList::AT_SysVABI: @@ -5857,6 +5954,15 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_OpenCLImageAccess: handleSimpleAttribute(S, D, Attr); break; + case AttributeList::AT_SwiftContext: + handleParameterABIAttr(S, D, Attr, ParameterABI::SwiftContext); + break; + case AttributeList::AT_SwiftErrorResult: + handleParameterABIAttr(S, D, Attr, ParameterABI::SwiftErrorResult); + break; + case AttributeList::AT_SwiftIndirectResult: + handleParameterABIAttr(S, D, Attr, ParameterABI::SwiftIndirectResult); + break; case AttributeList::AT_InternalLinkage: handleInternalLinkageAttr(S, D, Attr); break; diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 2be79da6da5..fe99ebbaa7a 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -273,6 +273,12 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs, } } + if (auto ABIAttr = dyn_cast(TmplAttr)) { + AddParameterABIAttr(ABIAttr->getRange(), New, ABIAttr->getABI(), + ABIAttr->getSpellingListIndex()); + continue; + } + if (isa(TmplAttr) || isa(TmplAttr)) { AddNSConsumedAttr(TmplAttr->getRange(), New, TmplAttr->getSpellingListIndex(), diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index a77edf4f762..829506259c9 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -107,6 +107,7 @@ static void diagnoseBadTypeAttribute(Sema &S, const AttributeList &attr, case AttributeList::AT_StdCall: \ case AttributeList::AT_ThisCall: \ case AttributeList::AT_Pascal: \ + case AttributeList::AT_SwiftCall: \ case AttributeList::AT_VectorCall: \ case AttributeList::AT_MSABI: \ case AttributeList::AT_SysVABI: \ @@ -2259,6 +2260,74 @@ bool Sema::CheckFunctionReturnType(QualType T, SourceLocation Loc) { return false; } +/// Check the extended parameter information. Most of the necessary +/// checking should occur when applying the parameter attribute; the +/// only other checks required are positional restrictions. +static void checkExtParameterInfos(Sema &S, ArrayRef paramTypes, + const FunctionProtoType::ExtProtoInfo &EPI, + llvm::function_ref getParamLoc) { + assert(EPI.ExtParameterInfos && "shouldn't get here without param infos"); + + bool hasCheckedSwiftCall = false; + auto checkForSwiftCC = [&](unsigned paramIndex) { + // Only do this once. + if (hasCheckedSwiftCall) return; + hasCheckedSwiftCall = true; + if (EPI.ExtInfo.getCC() == CC_Swift) return; + S.Diag(getParamLoc(paramIndex), diag::err_swift_param_attr_not_swiftcall) + << getParameterABISpelling(EPI.ExtParameterInfos[paramIndex].getABI()); + }; + + for (size_t paramIndex = 0, numParams = paramTypes.size(); + paramIndex != numParams; ++paramIndex) { + switch (EPI.ExtParameterInfos[paramIndex].getABI()) { + // Nothing interesting to check for orindary-ABI parameters. + case ParameterABI::Ordinary: + continue; + + // swift_indirect_result parameters must be a prefix of the function + // arguments. + case ParameterABI::SwiftIndirectResult: + checkForSwiftCC(paramIndex); + if (paramIndex != 0 && + EPI.ExtParameterInfos[paramIndex - 1].getABI() + != ParameterABI::SwiftIndirectResult) { + S.Diag(getParamLoc(paramIndex), + diag::err_swift_indirect_result_not_first); + } + continue; + + // swift_context parameters must be the last parameter except for + // a possible swift_error parameter. + case ParameterABI::SwiftContext: + checkForSwiftCC(paramIndex); + if (!(paramIndex == numParams - 1 || + (paramIndex == numParams - 2 && + EPI.ExtParameterInfos[numParams - 1].getABI() + == ParameterABI::SwiftErrorResult))) { + S.Diag(getParamLoc(paramIndex), + diag::err_swift_context_not_before_swift_error_result); + } + continue; + + // swift_error parameters must be the last parameter. + case ParameterABI::SwiftErrorResult: + checkForSwiftCC(paramIndex); + if (paramIndex != numParams - 1) { + S.Diag(getParamLoc(paramIndex), + diag::err_swift_error_result_not_last); + } else if (paramIndex == 0 || + EPI.ExtParameterInfos[paramIndex - 1].getABI() + != ParameterABI::SwiftContext) { + S.Diag(getParamLoc(paramIndex), + diag::err_swift_error_result_not_after_swift_context); + } + continue; + } + llvm_unreachable("bad ABI kind"); + } +} + QualType Sema::BuildFunctionType(QualType T, MutableArrayRef ParamTypes, SourceLocation Loc, DeclarationName Entity, @@ -2283,6 +2352,11 @@ QualType Sema::BuildFunctionType(QualType T, ParamTypes[Idx] = ParamType; } + if (EPI.ExtParameterInfos) { + checkExtParameterInfos(*this, ParamTypes, EPI, + [=](unsigned i) { return Loc; }); + } + if (Invalid) return QualType(); @@ -4068,11 +4142,20 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, HasAnyInterestingExtParameterInfos = true; } + if (auto attr = Param->getAttr()) { + ExtParameterInfos[i] = + ExtParameterInfos[i].withABI(attr->getABI()); + HasAnyInterestingExtParameterInfos = true; + } + ParamTys.push_back(ParamTy); } - if (HasAnyInterestingExtParameterInfos) + if (HasAnyInterestingExtParameterInfos) { EPI.ExtParameterInfos = ExtParameterInfos.data(); + checkExtParameterInfos(S, ParamTys, EPI, + [&](unsigned i) { return FTI.Params[i].Param->getLocation(); }); + } SmallVector Exceptions; SmallVector DynamicExceptions; @@ -4532,6 +4615,8 @@ static AttributeList::Kind getAttrListKind(AttributedType::Kind kind) { return AttributeList::AT_ThisCall; case AttributedType::attr_pascal: return AttributeList::AT_Pascal; + case AttributedType::attr_swiftcall: + return AttributeList::AT_SwiftCall; case AttributedType::attr_vectorcall: return AttributeList::AT_VectorCall; case AttributedType::attr_pcs: @@ -5861,6 +5946,8 @@ static AttributedType::Kind getCCTypeAttrKind(AttributeList &Attr) { return AttributedType::attr_thiscall; case AttributeList::AT_Pascal: return AttributedType::attr_pascal; + case AttributeList::AT_SwiftCall: + return AttributedType::attr_swiftcall; case AttributeList::AT_VectorCall: return AttributedType::attr_vectorcall; case AttributeList::AT_Pcs: { diff --git a/test/Sema/attr-swiftcall.c b/test/Sema/attr-swiftcall.c new file mode 100644 index 00000000000..3458167cf2e --- /dev/null +++ b/test/Sema/attr-swiftcall.c @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -verify %s + +#define SWIFTCALL __attribute__((swiftcall)) +#define INDIRECT_RESULT __attribute__((swift_indirect_result)) +#define ERROR_RESULT __attribute__((swift_error_result)) +#define CONTEXT __attribute__((swift_context)) + +int notAFunction SWIFTCALL; // expected-warning {{'swiftcall' only applies to function types; type here is 'int'}} +void variadic(int x, ...) SWIFTCALL; // expected-error {{variadic function cannot use swiftcall calling convention}} +void unprototyped() SWIFTCALL; // expected-error {{function with no prototype cannot use the swiftcall calling convention}} +void multiple_ccs(int x) SWIFTCALL __attribute__((vectorcall)); // expected-error {{vectorcall and swiftcall attributes are not compatible}} +void (*functionPointer)(void) SWIFTCALL; + +void indirect_result_nonswift(INDIRECT_RESULT void *out); // expected-error {{'swift_indirect_result' parameter can only be used with swiftcall calling convention}} +void indirect_result_bad_position(int first, INDIRECT_RESULT void *out) SWIFTCALL; // expected-error {{'swift_indirect_result' parameters must be first parameters of function}} +void indirect_result_bad_type(INDIRECT_RESULT int out) SWIFTCALL; // expected-error {{'swift_indirect_result' parameter must have pointer type; type here is 'int'}} +void indirect_result_single(INDIRECT_RESULT void *out) SWIFTCALL; +void indirect_result_multiple(INDIRECT_RESULT void *out1, INDIRECT_RESULT void *out2) SWIFTCALL; + +void error_result_nonswift(ERROR_RESULT void **error); // expected-error {{'swift_error_result' parameter can only be used with swiftcall calling convention}} expected-error{{'swift_error_result' parameter must follow 'swift_context' parameter}} +void error_result_bad_position(ERROR_RESULT void **error, int last) SWIFTCALL; // expected-error {{'swift_error_result' parameter must be last parameter of function}} +void error_result_bad_position2(int first, ERROR_RESULT void **error) SWIFTCALL; // expected-error {{'swift_error_result' parameter must follow 'swift_context' parameter}} +void error_result_bad_type(CONTEXT void *context, ERROR_RESULT int error) SWIFTCALL; // expected-error {{'swift_error_result' parameter must have pointer to unqualified pointer type; type here is 'int'}} +void error_result_bad_type2(CONTEXT void *context, ERROR_RESULT int *error) SWIFTCALL; // expected-error {{'swift_error_result' parameter must have pointer to unqualified pointer type; type here is 'int *'}} +void error_result_okay(int a, int b, CONTEXT void *context, ERROR_RESULT void **error) SWIFTCALL; + +void context_nonswift(CONTEXT void *context); // expected-error {{'swift_context' parameter can only be used with swiftcall calling convention}} +void context_bad_position(CONTEXT void *context, int x) SWIFTCALL; // expected-error {{'swift_context' parameter can only be followed by 'swift_error_result' parameter}} +void context_bad_type(CONTEXT int context) SWIFTCALL; // expected-error {{'swift_context' parameter must have pointer type; type here is 'int'}} +void context_okay(CONTEXT void *context) SWIFTCALL; diff --git a/test/SemaCXX/attr-swiftcall.cpp b/test/SemaCXX/attr-swiftcall.cpp new file mode 100644 index 00000000000..fd17aae1852 --- /dev/null +++ b/test/SemaCXX/attr-swiftcall.cpp @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -verify %s + +#define SWIFTCALL __attribute__((swiftcall)) +#define INDIRECT_RESULT __attribute__((swift_indirect_result)) +#define ERROR_RESULT __attribute__((swift_error_result)) +#define CONTEXT __attribute__((swift_context)) + +int notAFunction SWIFTCALL; // expected-warning {{'swiftcall' only applies to function types; type here is 'int'}} +void variadic(int x, ...) SWIFTCALL; // expected-error {{variadic function cannot use swiftcall calling convention}} +void multiple_ccs(int x) SWIFTCALL __attribute__((vectorcall)); // expected-error {{vectorcall and swiftcall attributes are not compatible}} +void (*functionPointer)(void) SWIFTCALL; + +void indirect_result_nonswift(INDIRECT_RESULT void *out); // expected-error {{'swift_indirect_result' parameter can only be used with swiftcall calling convention}} +void indirect_result_bad_position(int first, INDIRECT_RESULT void *out) SWIFTCALL; // expected-error {{'swift_indirect_result' parameters must be first parameters of function}} +void indirect_result_bad_type(INDIRECT_RESULT int out) SWIFTCALL; // expected-error {{'swift_indirect_result' parameter must have pointer type; type here is 'int'}} +void indirect_result_single(INDIRECT_RESULT void *out) SWIFTCALL; +void indirect_result_multiple(INDIRECT_RESULT void *out1, INDIRECT_RESULT void *out2) SWIFTCALL; + +void error_result_nonswift(ERROR_RESULT void **error); // expected-error {{'swift_error_result' parameter can only be used with swiftcall calling convention}} expected-error{{'swift_error_result' parameter must follow 'swift_context' parameter}} +void error_result_bad_position(ERROR_RESULT void **error, int last) SWIFTCALL; // expected-error {{'swift_error_result' parameter must be last parameter of function}} +void error_result_bad_position2(int first, ERROR_RESULT void **error) SWIFTCALL; // expected-error {{'swift_error_result' parameter must follow 'swift_context' parameter}} +void error_result_bad_type(CONTEXT void *context, ERROR_RESULT int error) SWIFTCALL; // expected-error {{'swift_error_result' parameter must have pointer to unqualified pointer type; type here is 'int'}} +void error_result_bad_type2(CONTEXT void *context, ERROR_RESULT int *error) SWIFTCALL; // expected-error {{'swift_error_result' parameter must have pointer to unqualified pointer type; type here is 'int *'}} +void error_result_okay(int a, int b, CONTEXT void *context, ERROR_RESULT void **error) SWIFTCALL; + +void context_nonswift(CONTEXT void *context); // expected-error {{'swift_context' parameter can only be used with swiftcall calling convention}} +void context_bad_position(CONTEXT void *context, int x) SWIFTCALL; // expected-error {{'swift_context' parameter can only be followed by 'swift_error_result' parameter}} +void context_bad_type(CONTEXT int context) SWIFTCALL; // expected-error {{'swift_context' parameter must have pointer type; type here is 'int'}} +void context_okay(CONTEXT void *context) SWIFTCALL; + +template void indirect_result_temp_okay1(INDIRECT_RESULT T *out) SWIFTCALL; +template void indirect_result_temp_okay2(INDIRECT_RESULT T out) SWIFTCALL; // expected-note {{candidate template ignored: substitution failure [with T = int]: 'swift_indirect_result' parameter must have pointer type; type here is 'int'}} +void test_indirect_result_temp(void *out) { + indirect_result_temp_okay1(out); + indirect_result_temp_okay2(out); + indirect_result_temp_okay2(1); // expected-error {{no matching function for call to 'indirect_result_temp_okay2'}} +} diff --git a/tools/libclang/CXType.cpp b/tools/libclang/CXType.cpp index d6d5daa0f9d..5cde2360552 100644 --- a/tools/libclang/CXType.cpp +++ b/tools/libclang/CXType.cpp @@ -533,6 +533,7 @@ CXCallingConv clang_getFunctionTypeCallingConv(CXType X) { TCALLINGCONV(AAPCS); TCALLINGCONV(AAPCS_VFP); TCALLINGCONV(IntelOclBicc); + TCALLINGCONV(Swift); TCALLINGCONV(PreserveMost); TCALLINGCONV(PreserveAll); case CC_SpirFunction: return CXCallingConv_Unexposed; diff --git a/utils/TableGen/ClangAttrEmitter.cpp b/utils/TableGen/ClangAttrEmitter.cpp index 6f80eb6f061..b4ef3787863 100644 --- a/utils/TableGen/ClangAttrEmitter.cpp +++ b/utils/TableGen/ClangAttrEmitter.cpp @@ -1760,7 +1760,8 @@ namespace { static const AttrClassDescriptor AttrClassDescriptors[] = { { "ATTR", "Attr" }, { "INHERITABLE_ATTR", "InheritableAttr" }, - { "INHERITABLE_PARAM_ATTR", "InheritableParamAttr" } + { "INHERITABLE_PARAM_ATTR", "InheritableParamAttr" }, + { "PARAMETER_ABI_ATTR", "ParameterABIAttr" } }; static void emitDefaultDefine(raw_ostream &OS, StringRef name, From 6a04d0a91034cbf2d71cdc4f277dbc266599b1c0 Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 11 Mar 2016 04:30:31 +0000 Subject: [PATCH 463/742] Preserve ExtParameterInfos into CGFunctionInfo. As part of this, make the function-arrangement interfaces a little simpler and more semantic. NFC. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263191 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/CanonicalType.h | 3 + include/clang/CodeGen/CGFunctionInfo.h | 34 +++- lib/CodeGen/CGAtomic.cpp | 3 +- lib/CodeGen/CGBlocks.cpp | 21 ++- lib/CodeGen/CGBuiltin.cpp | 4 +- lib/CodeGen/CGCall.cpp | 227 ++++++++++++++++++++----- lib/CodeGen/CGDeclCXX.cpp | 4 +- lib/CodeGen/CGException.cpp | 10 +- lib/CodeGen/CGObjC.cpp | 46 ++--- lib/CodeGen/CGObjCMac.cpp | 31 ++-- lib/CodeGen/CGObjCRuntime.cpp | 20 +-- lib/CodeGen/CGOpenMPRuntime.cpp | 34 ++-- lib/CodeGen/CGStmt.cpp | 3 +- lib/CodeGen/CGStmtOpenMP.cpp | 3 +- lib/CodeGen/CodeGenABITypes.cpp | 2 +- lib/CodeGen/CodeGenTypes.h | 62 +++++-- lib/CodeGen/ItaniumCXXABI.cpp | 9 +- 17 files changed, 331 insertions(+), 185 deletions(-) diff --git a/include/clang/AST/CanonicalType.h b/include/clang/AST/CanonicalType.h index b25800bfedb..77510afeec1 100644 --- a/include/clang/AST/CanonicalType.h +++ b/include/clang/AST/CanonicalType.h @@ -484,6 +484,9 @@ struct CanProxyAdaptor LLVM_CLANG_CANPROXY_TYPE_ACCESSOR(getReturnType) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(FunctionType::ExtInfo, getExtInfo) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(unsigned, getNumParams) + LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, hasExtParameterInfos) + LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR( + ArrayRef, getExtParameterInfos) CanQualType getParamType(unsigned i) const { return CanQualType::CreateUnsafe(this->getTypePtr()->getParamType(i)); } diff --git a/include/clang/CodeGen/CGFunctionInfo.h b/include/clang/CodeGen/CGFunctionInfo.h index bb6ceb43514..9af28975bdd 100644 --- a/include/clang/CodeGen/CGFunctionInfo.h +++ b/include/clang/CodeGen/CGFunctionInfo.h @@ -338,6 +338,7 @@ class CGFunctionInfo : public llvm::FoldingSetNode { CanQualType type; ABIArgInfo info; }; + typedef FunctionProtoType::ExtParameterInfo ExtParameterInfo; /// The LLVM::CallingConv to use for this function (as specified by the /// user). @@ -371,7 +372,8 @@ class CGFunctionInfo : public llvm::FoldingSetNode { /// The struct representing all arguments passed in memory. Only used when /// passing non-trivial types with inalloca. Not part of the profile. llvm::StructType *ArgStruct; - unsigned ArgStructAlign; + unsigned ArgStructAlign : 31; + unsigned HasExtParameterInfos : 1; unsigned NumArgs; ArgInfo *getArgsBuffer() { @@ -381,6 +383,14 @@ class CGFunctionInfo : public llvm::FoldingSetNode { return reinterpret_cast(this + 1); } + ExtParameterInfo *getExtParameterInfosBuffer() { + return reinterpret_cast(getArgsBuffer() + NumArgs + 1); + } + const ExtParameterInfo *getExtParameterInfosBuffer() const{ + return reinterpret_cast( + getArgsBuffer() + NumArgs + 1); + } + CGFunctionInfo() : Required(RequiredArgs::All) {} public: @@ -388,6 +398,7 @@ class CGFunctionInfo : public llvm::FoldingSetNode { bool instanceMethod, bool chainCall, const FunctionType::ExtInfo &extInfo, + ArrayRef paramInfos, CanQualType resultType, ArrayRef argTypes, RequiredArgs required); @@ -460,6 +471,16 @@ class CGFunctionInfo : public llvm::FoldingSetNode { ABIArgInfo &getReturnInfo() { return getArgsBuffer()[0].info; } const ABIArgInfo &getReturnInfo() const { return getArgsBuffer()[0].info; } + ArrayRef getExtParameterInfos() const { + if (!HasExtParameterInfos) return {}; + return llvm::makeArrayRef(getExtParameterInfosBuffer(), NumArgs); + } + ExtParameterInfo getExtParameterInfo(unsigned argIndex) const { + assert(argIndex <= NumArgs); + if (!HasExtParameterInfos) return ExtParameterInfo(); + return getExtParameterInfos()[argIndex]; + } + /// \brief Return true if this function uses inalloca arguments. bool usesInAlloca() const { return ArgStruct; } @@ -482,6 +503,11 @@ class CGFunctionInfo : public llvm::FoldingSetNode { ID.AddBoolean(HasRegParm); ID.AddInteger(RegParm); ID.AddInteger(Required.getOpaqueData()); + ID.AddBoolean(HasExtParameterInfos); + if (HasExtParameterInfos) { + for (auto paramInfo : getExtParameterInfos()) + ID.AddInteger(paramInfo.getOpaqueValue()); + } getReturnType().Profile(ID); for (const auto &I : arguments()) I.type.Profile(ID); @@ -490,6 +516,7 @@ class CGFunctionInfo : public llvm::FoldingSetNode { bool InstanceMethod, bool ChainCall, const FunctionType::ExtInfo &info, + ArrayRef paramInfos, RequiredArgs required, CanQualType resultType, ArrayRef argTypes) { @@ -501,6 +528,11 @@ class CGFunctionInfo : public llvm::FoldingSetNode { ID.AddBoolean(info.getHasRegParm()); ID.AddInteger(info.getRegParm()); ID.AddInteger(required.getOpaqueData()); + ID.AddBoolean(!paramInfos.empty()); + if (!paramInfos.empty()) { + for (auto paramInfo : paramInfos) + ID.AddInteger(paramInfo.getOpaqueValue()); + } resultType.Profile(ID); for (ArrayRef::iterator i = argTypes.begin(), e = argTypes.end(); i != e; ++i) { diff --git a/lib/CodeGen/CGAtomic.cpp b/lib/CodeGen/CGAtomic.cpp index 1ef5d1035a0..f6552957e70 100644 --- a/lib/CodeGen/CGAtomic.cpp +++ b/lib/CodeGen/CGAtomic.cpp @@ -323,8 +323,7 @@ static RValue emitAtomicLibcall(CodeGenFunction &CGF, QualType resultType, CallArgList &args) { const CGFunctionInfo &fnInfo = - CGF.CGM.getTypes().arrangeFreeFunctionCall(resultType, args, - FunctionType::ExtInfo(), RequiredArgs::All); + CGF.CGM.getTypes().arrangeBuiltinFunctionCall(resultType, args); llvm::FunctionType *fnTy = CGF.CGM.getTypes().GetFunctionType(fnInfo); llvm::Constant *fn = CGF.CGM.CreateRuntimeFunction(fnTy, fnName); return CGF.EmitCall(fnInfo, fn, ReturnValueSlot(), args); diff --git a/lib/CodeGen/CGBlocks.cpp b/lib/CodeGen/CGBlocks.cpp index a74294e7d62..ea164aad915 100644 --- a/lib/CodeGen/CGBlocks.cpp +++ b/lib/CodeGen/CGBlocks.cpp @@ -1174,9 +1174,8 @@ CodeGenFunction::GenerateBlockFunction(GlobalDecl GD, // Create the function declaration. const FunctionProtoType *fnType = blockInfo.getBlockExpr()->getFunctionType(); - const CGFunctionInfo &fnInfo = CGM.getTypes().arrangeFreeFunctionDeclaration( - fnType->getReturnType(), args, fnType->getExtInfo(), - fnType->isVariadic()); + const CGFunctionInfo &fnInfo = + CGM.getTypes().arrangeBlockFunctionDeclaration(fnType, args); if (CGM.ReturnSlotInterferesWithArgs(fnInfo)) blockInfo.UsesStret = true; @@ -1329,8 +1328,8 @@ CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) { C.VoidPtrTy); args.push_back(&srcDecl); - const CGFunctionInfo &FI = CGM.getTypes().arrangeFreeFunctionDeclaration( - C.VoidTy, args, FunctionType::ExtInfo(), /*variadic=*/false); + const CGFunctionInfo &FI = + CGM.getTypes().arrangeBuiltinFunctionDeclaration(C.VoidTy, args); // FIXME: it would be nice if these were mergeable with things with // identical semantics. @@ -1505,8 +1504,8 @@ CodeGenFunction::GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo) { C.VoidPtrTy); args.push_back(&srcDecl); - const CGFunctionInfo &FI = CGM.getTypes().arrangeFreeFunctionDeclaration( - C.VoidTy, args, FunctionType::ExtInfo(), /*variadic=*/false); + const CGFunctionInfo &FI = + CGM.getTypes().arrangeBuiltinFunctionDeclaration(C.VoidTy, args); // FIXME: We'd like to put these into a mergable by content, with // internal linkage. @@ -1791,8 +1790,8 @@ generateByrefCopyHelper(CodeGenFunction &CGF, const BlockByrefInfo &byrefInfo, Context.VoidPtrTy); args.push_back(&src); - const CGFunctionInfo &FI = CGF.CGM.getTypes().arrangeFreeFunctionDeclaration( - R, args, FunctionType::ExtInfo(), /*variadic=*/false); + const CGFunctionInfo &FI = + CGF.CGM.getTypes().arrangeBuiltinFunctionDeclaration(R, args); llvm::FunctionType *LTy = CGF.CGM.getTypes().GetFunctionType(FI); @@ -1864,8 +1863,8 @@ generateByrefDisposeHelper(CodeGenFunction &CGF, Context.VoidPtrTy); args.push_back(&src); - const CGFunctionInfo &FI = CGF.CGM.getTypes().arrangeFreeFunctionDeclaration( - R, args, FunctionType::ExtInfo(), /*variadic=*/false); + const CGFunctionInfo &FI = + CGF.CGM.getTypes().arrangeBuiltinFunctionDeclaration(R, args); llvm::FunctionType *LTy = CGF.CGM.getTypes().GetFunctionType(FI); diff --git a/lib/CodeGen/CGBuiltin.cpp b/lib/CodeGen/CGBuiltin.cpp index 787ac5361bb..c73aa222b78 100644 --- a/lib/CodeGen/CGBuiltin.cpp +++ b/lib/CodeGen/CGBuiltin.cpp @@ -1294,9 +1294,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, Args.add(RValue::get(llvm::Constant::getNullValue(VoidPtrTy)), getContext().VoidPtrTy); const CGFunctionInfo &FuncInfo = - CGM.getTypes().arrangeFreeFunctionCall(E->getType(), Args, - FunctionType::ExtInfo(), - RequiredArgs::All); + CGM.getTypes().arrangeBuiltinFunctionCall(E->getType(), Args); llvm::FunctionType *FTy = CGM.getTypes().GetFunctionType(FuncInfo); llvm::Constant *Func = CGM.CreateRuntimeFunction(FTy, LibCallName); return EmitCall(FuncInfo, Func, ReturnValueSlot(), Args); diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index 35b0585069b..a4f2c1a3400 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -93,15 +93,25 @@ CodeGenTypes::arrangeFreeFunctionType(CanQual FTNP) { return arrangeLLVMFunctionInfo(FTNP->getReturnType().getUnqualifiedType(), /*instanceMethod=*/false, /*chainCall=*/false, None, - FTNP->getExtInfo(), RequiredArgs(0)); + FTNP->getExtInfo(), {}, RequiredArgs(0)); } /// Adds the formal paramaters in FPT to the given prefix. If any parameter in /// FPT has pass_object_size attrs, then we'll add parameters for those, too. static void appendParameterTypes(const CodeGenTypes &CGT, SmallVectorImpl &prefix, - const CanQual &FPT, + SmallVectorImpl ¶mInfos, + CanQual FPT, const FunctionDecl *FD) { + // Fill out paramInfos. + if (FPT->hasExtParameterInfos() || !paramInfos.empty()) { + assert(paramInfos.size() <= prefix.size()); + auto protoParamInfos = FPT->getExtParameterInfos(); + paramInfos.reserve(prefix.size() + protoParamInfos.size()); + paramInfos.resize(prefix.size()); + paramInfos.append(paramInfos.begin(), paramInfos.end()); + } + // Fast path: unknown target. if (FD == nullptr) { prefix.append(FPT->param_type_begin(), FPT->param_type_end()); @@ -128,13 +138,16 @@ arrangeLLVMFunctionInfo(CodeGenTypes &CGT, bool instanceMethod, SmallVectorImpl &prefix, CanQual FTP, const FunctionDecl *FD) { + SmallVector paramInfos; RequiredArgs required = RequiredArgs::forPrototypePlus(FTP, prefix.size()); // FIXME: Kill copy. - appendParameterTypes(CGT, prefix, FTP, FD); + appendParameterTypes(CGT, prefix, paramInfos, FTP, FD); CanQualType resultType = FTP->getReturnType().getUnqualifiedType(); + return CGT.arrangeLLVMFunctionInfo(resultType, instanceMethod, /*chainCall=*/false, prefix, - FTP->getExtInfo(), required); + FTP->getExtInfo(), paramInfos, + required); } /// Arrange the argument and result information for a value of the @@ -233,6 +246,7 @@ CodeGenTypes::arrangeCXXStructorDeclaration(const CXXMethodDecl *MD, StructorType Type) { SmallVector argTypes; + SmallVector paramInfos; argTypes.push_back(GetThisType(Context, MD->getParent())); GlobalDecl GD; @@ -246,7 +260,7 @@ CodeGenTypes::arrangeCXXStructorDeclaration(const CXXMethodDecl *MD, CanQual FTP = GetFormalType(MD); // Add the formal parameters. - appendParameterTypes(*this, argTypes, FTP, MD); + appendParameterTypes(*this, argTypes, paramInfos, FTP, MD); TheCXXABI.buildStructorSignature(MD, Type, argTypes); @@ -261,7 +275,53 @@ CodeGenTypes::arrangeCXXStructorDeclaration(const CXXMethodDecl *MD, : Context.VoidTy; return arrangeLLVMFunctionInfo(resultType, /*instanceMethod=*/true, /*chainCall=*/false, argTypes, extInfo, - required); + paramInfos, required); +} + +static SmallVector +getArgTypesForCall(ASTContext &ctx, const CallArgList &args) { + SmallVector argTypes; + for (auto &arg : args) + argTypes.push_back(ctx.getCanonicalParamType(arg.Ty)); + return argTypes; +} + +static SmallVector +getArgTypesForDeclaration(ASTContext &ctx, const FunctionArgList &args) { + SmallVector argTypes; + for (auto &arg : args) + argTypes.push_back(ctx.getCanonicalParamType(arg->getType())); + return argTypes; +} + +static void addExtParameterInfosForCall( + llvm::SmallVectorImpl ¶mInfos, + const FunctionProtoType *proto, + unsigned prefixArgs, + unsigned totalArgs) { + assert(proto->hasExtParameterInfos()); + assert(paramInfos.size() <= prefixArgs); + assert(proto->getNumParams() + prefixArgs <= totalArgs); + + // Add default infos for any prefix args that don't already have infos. + paramInfos.resize(prefixArgs); + + // Add infos for the prototype. + auto protoInfos = proto->getExtParameterInfos(); + paramInfos.append(protoInfos.begin(), protoInfos.end()); + + // Add default infos for the variadic arguments. + paramInfos.resize(totalArgs); +} + +static llvm::SmallVector +getExtParameterInfosForCall(const FunctionProtoType *proto, + unsigned prefixArgs, unsigned totalArgs) { + llvm::SmallVector result; + if (proto->hasExtParameterInfos()) { + addExtParameterInfosForCall(result, proto, prefixArgs, totalArgs); + } + return result; } /// Arrange a call to a C++ method, passing the given arguments. @@ -285,9 +345,11 @@ CodeGenTypes::arrangeCXXConstructorCall(const CallArgList &args, : Context.VoidTy; FunctionType::ExtInfo Info = FPT->getExtInfo(); + auto ParamInfos = getExtParameterInfosForCall(FPT.getTypePtr(), 1 + ExtraArgs, + ArgTypes.size()); return arrangeLLVMFunctionInfo(ResultType, /*instanceMethod=*/true, /*chainCall=*/false, ArgTypes, Info, - Required); + ParamInfos, Required); } /// Arrange the argument and result information for the declaration or @@ -308,7 +370,7 @@ CodeGenTypes::arrangeFunctionDeclaration(const FunctionDecl *FD) { CanQual noProto = FTy.getAs(); return arrangeLLVMFunctionInfo( noProto->getReturnType(), /*instanceMethod=*/false, - /*chainCall=*/false, None, noProto->getExtInfo(), RequiredArgs::All); + /*chainCall=*/false, None, noProto->getExtInfo(), {},RequiredArgs::All); } assert(isa(FTy)); @@ -354,7 +416,18 @@ CodeGenTypes::arrangeObjCMessageSendSignature(const ObjCMethodDecl *MD, return arrangeLLVMFunctionInfo( GetReturnType(MD->getReturnType()), /*instanceMethod=*/false, - /*chainCall=*/false, argTys, einfo, required); + /*chainCall=*/false, argTys, einfo, {}, required); +} + +const CGFunctionInfo & +CodeGenTypes::arrangeUnprototypedObjCMessageSend(QualType returnType, + const CallArgList &args) { + auto argTypes = getArgTypesForCall(Context, args); + FunctionType::ExtInfo einfo; + + return arrangeLLVMFunctionInfo( + GetReturnType(returnType), /*instanceMethod=*/false, + /*chainCall=*/false, argTypes, einfo, {}, RequiredArgs::All); } const CGFunctionInfo & @@ -383,7 +456,7 @@ CodeGenTypes::arrangeMSMemberPointerThunk(const CXXMethodDecl *MD) { CanQualType ArgTys[] = { GetThisType(Context, MD->getParent()) }; return arrangeLLVMFunctionInfo(Context.VoidTy, /*instanceMethod=*/false, /*chainCall=*/false, ArgTys, - FTP->getExtInfo(), RequiredArgs(1)); + FTP->getExtInfo(), {}, RequiredArgs(1)); } const CGFunctionInfo & @@ -403,7 +476,8 @@ CodeGenTypes::arrangeMSCtorClosure(const CXXConstructorDecl *CD, /*IsVariadic=*/false, /*IsCXXMethod=*/true); return arrangeLLVMFunctionInfo(Context.VoidTy, /*instanceMethod=*/true, /*chainCall=*/false, ArgTys, - FunctionType::ExtInfo(CC), RequiredArgs::All); + FunctionType::ExtInfo(CC), {}, + RequiredArgs::All); } /// Arrange a call as unto a free function, except possibly with an @@ -417,6 +491,8 @@ arrangeFreeFunctionLikeCall(CodeGenTypes &CGT, bool chainCall) { assert(args.size() >= numExtraRequiredArgs); + llvm::SmallVector paramInfos; + // In most cases, there are no optional arguments. RequiredArgs required = RequiredArgs::All; @@ -426,6 +502,10 @@ arrangeFreeFunctionLikeCall(CodeGenTypes &CGT, if (proto->isVariadic()) required = RequiredArgs(proto->getNumParams() + numExtraRequiredArgs); + if (proto->hasExtParameterInfos()) + addExtParameterInfosForCall(paramInfos, proto, numExtraRequiredArgs, + args.size()); + // If we don't have a prototype at all, but we're supposed to // explicitly use the variadic convention for unprototyped calls, // treat all of the arguments as required but preserve the nominal @@ -442,7 +522,8 @@ arrangeFreeFunctionLikeCall(CodeGenTypes &CGT, argTypes.push_back(CGT.getContext().getCanonicalParamType(arg.Ty)); return CGT.arrangeLLVMFunctionInfo(GetReturnType(fnType->getReturnType()), /*instanceMethod=*/false, chainCall, - argTypes, fnType->getExtInfo(), required); + argTypes, fnType->getExtInfo(), paramInfos, + required); } /// Figure out the rules for calling a function with the given formal @@ -457,7 +538,7 @@ CodeGenTypes::arrangeFreeFunctionCall(const CallArgList &args, chainCall ? 1 : 0, chainCall); } -/// A block function call is essentially a free-function call with an +/// A block function is essentially a free function with an /// extra implicit argument. const CGFunctionInfo & CodeGenTypes::arrangeBlockFunctionCall(const CallArgList &args, @@ -467,54 +548,99 @@ CodeGenTypes::arrangeBlockFunctionCall(const CallArgList &args, } const CGFunctionInfo & -CodeGenTypes::arrangeFreeFunctionCall(QualType resultType, - const CallArgList &args, - FunctionType::ExtInfo info, - RequiredArgs required) { +CodeGenTypes::arrangeBlockFunctionDeclaration(const FunctionProtoType *proto, + const FunctionArgList ¶ms) { + auto paramInfos = getExtParameterInfosForCall(proto, 1, params.size()); + auto argTypes = getArgTypesForDeclaration(Context, params); + + return arrangeLLVMFunctionInfo(GetReturnType(proto->getReturnType()), + /*instanceMethod*/ false, /*chainCall*/ false, + argTypes, proto->getExtInfo(), paramInfos, + RequiredArgs::forPrototypePlus(proto, 1)); +} + +const CGFunctionInfo & +CodeGenTypes::arrangeBuiltinFunctionCall(QualType resultType, + const CallArgList &args) { // FIXME: Kill copy. SmallVector argTypes; for (const auto &Arg : args) argTypes.push_back(Context.getCanonicalParamType(Arg.Ty)); return arrangeLLVMFunctionInfo( GetReturnType(resultType), /*instanceMethod=*/false, - /*chainCall=*/false, argTypes, info, required); + /*chainCall=*/false, argTypes, FunctionType::ExtInfo(), + /*paramInfos=*/ {}, RequiredArgs::All); } -/// Arrange a call to a C++ method, passing the given arguments. const CGFunctionInfo & -CodeGenTypes::arrangeCXXMethodCall(const CallArgList &args, - const FunctionProtoType *FPT, - RequiredArgs required) { - // FIXME: Kill copy. - SmallVector argTypes; - for (const auto &Arg : args) - argTypes.push_back(Context.getCanonicalParamType(Arg.Ty)); +CodeGenTypes::arrangeBuiltinFunctionDeclaration(QualType resultType, + const FunctionArgList &args) { + auto argTypes = getArgTypesForDeclaration(Context, args); + + return arrangeLLVMFunctionInfo( + GetReturnType(resultType), /*instanceMethod=*/false, /*chainCall=*/false, + argTypes, FunctionType::ExtInfo(), {}, RequiredArgs::All); +} - FunctionType::ExtInfo info = FPT->getExtInfo(); +const CGFunctionInfo & +CodeGenTypes::arrangeBuiltinFunctionDeclaration(CanQualType resultType, + ArrayRef argTypes) { return arrangeLLVMFunctionInfo( - GetReturnType(FPT->getReturnType()), /*instanceMethod=*/true, - /*chainCall=*/false, argTypes, info, required); + resultType, /*instanceMethod=*/false, /*chainCall=*/false, + argTypes, FunctionType::ExtInfo(), {}, RequiredArgs::All); } -const CGFunctionInfo &CodeGenTypes::arrangeFreeFunctionDeclaration( - QualType resultType, const FunctionArgList &args, - const FunctionType::ExtInfo &info, bool isVariadic) { + +/// Arrange a call to a C++ method, passing the given arguments. +const CGFunctionInfo & +CodeGenTypes::arrangeCXXMethodCall(const CallArgList &args, + const FunctionProtoType *proto, + RequiredArgs required) { + unsigned numRequiredArgs = + (proto->isVariadic() ? required.getNumRequiredArgs() : args.size()); + unsigned numPrefixArgs = numRequiredArgs - proto->getNumParams(); + auto paramInfos = + getExtParameterInfosForCall(proto, numPrefixArgs, args.size()); + // FIXME: Kill copy. - SmallVector argTypes; - for (auto Arg : args) - argTypes.push_back(Context.getCanonicalParamType(Arg->getType())); + auto argTypes = getArgTypesForCall(Context, args); - RequiredArgs required = - (isVariadic ? RequiredArgs(args.size()) : RequiredArgs::All); + FunctionType::ExtInfo info = proto->getExtInfo(); return arrangeLLVMFunctionInfo( - GetReturnType(resultType), /*instanceMethod=*/false, - /*chainCall=*/false, argTypes, info, required); + GetReturnType(proto->getReturnType()), /*instanceMethod=*/true, + /*chainCall=*/false, argTypes, info, paramInfos, required); } const CGFunctionInfo &CodeGenTypes::arrangeNullaryFunction() { return arrangeLLVMFunctionInfo( getContext().VoidTy, /*instanceMethod=*/false, /*chainCall=*/false, - None, FunctionType::ExtInfo(), RequiredArgs::All); + None, FunctionType::ExtInfo(), {}, RequiredArgs::All); +} + +const CGFunctionInfo & +CodeGenTypes::arrangeCall(const CGFunctionInfo &signature, + const CallArgList &args) { + assert(signature.arg_size() <= args.size()); + if (signature.arg_size() == args.size()) + return signature; + + SmallVector paramInfos; + auto sigParamInfos = signature.getExtParameterInfos(); + if (!sigParamInfos.empty()) { + paramInfos.append(sigParamInfos.begin(), sigParamInfos.end()); + paramInfos.resize(args.size()); + } + + auto argTypes = getArgTypesForCall(Context, args); + + assert(signature.getRequiredArgs().allowsOptionalArgs()); + return arrangeLLVMFunctionInfo(signature.getReturnType(), + signature.isInstanceMethod(), + signature.isChainCall(), + argTypes, + signature.getExtInfo(), + paramInfos, + signature.getRequiredArgs()); } /// Arrange the argument and result information for an abstract value @@ -526,25 +652,26 @@ CodeGenTypes::arrangeLLVMFunctionInfo(CanQualType resultType, bool chainCall, ArrayRef argTypes, FunctionType::ExtInfo info, + ArrayRef paramInfos, RequiredArgs required) { assert(std::all_of(argTypes.begin(), argTypes.end(), std::mem_fun_ref(&CanQualType::isCanonicalAsParam))); - unsigned CC = ClangCallConvToLLVMCallConv(info.getCC()); - // Lookup or create unique function info. llvm::FoldingSetNodeID ID; - CGFunctionInfo::Profile(ID, instanceMethod, chainCall, info, required, - resultType, argTypes); + CGFunctionInfo::Profile(ID, instanceMethod, chainCall, info, paramInfos, + required, resultType, argTypes); void *insertPos = nullptr; CGFunctionInfo *FI = FunctionInfos.FindNodeOrInsertPos(ID, insertPos); if (FI) return *FI; + unsigned CC = ClangCallConvToLLVMCallConv(info.getCC()); + // Construct the function info. We co-allocate the ArgInfos. FI = CGFunctionInfo::create(CC, instanceMethod, chainCall, info, - resultType, argTypes, required); + paramInfos, resultType, argTypes, required); FunctionInfos.InsertNode(FI, insertPos); bool inserted = FunctionsBeingProcessed.insert(FI).second; @@ -575,11 +702,16 @@ CGFunctionInfo *CGFunctionInfo::create(unsigned llvmCC, bool instanceMethod, bool chainCall, const FunctionType::ExtInfo &info, + ArrayRef paramInfos, CanQualType resultType, ArrayRef argTypes, RequiredArgs required) { + assert(paramInfos.empty() || paramInfos.size() == argTypes.size()); + void *buffer = operator new(sizeof(CGFunctionInfo) + - sizeof(ArgInfo) * (argTypes.size() + 1)); + sizeof(ArgInfo) * (argTypes.size() + 1) + + sizeof(ExtParameterInfo) * paramInfos.size()); + CGFunctionInfo *FI = new(buffer) CGFunctionInfo(); FI->CallingConvention = llvmCC; FI->EffectiveCallingConvention = llvmCC; @@ -594,9 +726,12 @@ CGFunctionInfo *CGFunctionInfo::create(unsigned llvmCC, FI->ArgStruct = nullptr; FI->ArgStructAlign = 0; FI->NumArgs = argTypes.size(); + FI->HasExtParameterInfos = !paramInfos.empty(); FI->getArgsBuffer()[0].type = resultType; for (unsigned i = 0, e = argTypes.size(); i != e; ++i) FI->getArgsBuffer()[i + 1].type = argTypes[i]; + for (unsigned i = 0, e = paramInfos.size(); i != e; ++i) + FI->getExtParameterInfosBuffer()[i] = paramInfos[i]; return FI; } diff --git a/lib/CodeGen/CGDeclCXX.cpp b/lib/CodeGen/CGDeclCXX.cpp index adba7316879..50454cabd9c 100644 --- a/lib/CodeGen/CGDeclCXX.cpp +++ b/lib/CodeGen/CGDeclCXX.cpp @@ -587,8 +587,8 @@ llvm::Function *CodeGenFunction::generateDestroyHelper( getContext().VoidPtrTy); args.push_back(&dst); - const CGFunctionInfo &FI = CGM.getTypes().arrangeFreeFunctionDeclaration( - getContext().VoidTy, args, FunctionType::ExtInfo(), /*variadic=*/false); + const CGFunctionInfo &FI = + CGM.getTypes().arrangeBuiltinFunctionDeclaration(getContext().VoidTy, args); llvm::FunctionType *FTy = CGM.getTypes().GetFunctionType(FI); llvm::Function *fn = CGM.CreateGlobalInitOrDestructFunction( FTy, "__cxx_global_array_dtor", FI, VD->getLocation()); diff --git a/lib/CodeGen/CGException.cpp b/lib/CodeGen/CGException.cpp index fce2e758196..fb51262300e 100644 --- a/lib/CodeGen/CGException.cpp +++ b/lib/CodeGen/CGException.cpp @@ -1422,12 +1422,8 @@ struct PerformSEHFinally final : EHScopeStack::Cleanup { Args.add(RValue::get(FP), ArgTys[1]); // Arrange a two-arg function info and type. - FunctionProtoType::ExtProtoInfo EPI; - const auto *FPT = cast( - Context.getFunctionType(Context.VoidTy, ArgTys, EPI)); const CGFunctionInfo &FnInfo = - CGM.getTypes().arrangeFreeFunctionCall(Args, FPT, - /*chainCall=*/false); + CGM.getTypes().arrangeBuiltinFunctionCall(Context.VoidTy, Args); CGF.EmitCall(FnInfo, OutlinedFinally, ReturnValueSlot(), Args); } @@ -1656,8 +1652,8 @@ void CodeGenFunction::startOutlinedSEHHelper(CodeGenFunction &ParentCGF, QualType RetTy = IsFilter ? getContext().LongTy : getContext().VoidTy; llvm::Function *ParentFn = ParentCGF.CurFn; - const CGFunctionInfo &FnInfo = CGM.getTypes().arrangeFreeFunctionDeclaration( - RetTy, Args, FunctionType::ExtInfo(), /*isVariadic=*/false); + const CGFunctionInfo &FnInfo = + CGM.getTypes().arrangeBuiltinFunctionDeclaration(RetTy, Args); llvm::FunctionType *FnTy = CGM.getTypes().GetFunctionType(FnInfo); llvm::Function *Fn = llvm::Function::Create( diff --git a/lib/CodeGen/CGObjC.cpp b/lib/CodeGen/CGObjC.cpp index 989d911a0cb..1c165594612 100644 --- a/lib/CodeGen/CGObjC.cpp +++ b/lib/CodeGen/CGObjC.cpp @@ -590,9 +590,7 @@ static void emitStructGetterCall(CodeGenFunction &CGF, ObjCIvarDecl *ivar, args.add(RValue::get(CGF.Builder.getInt1(hasStrong)), Context.BoolTy); llvm::Value *fn = CGF.CGM.getObjCRuntime().GetGetStructFunction(); - CGF.EmitCall(CGF.getTypes().arrangeFreeFunctionCall(Context.VoidTy, args, - FunctionType::ExtInfo(), - RequiredArgs::All), + CGF.EmitCall(CGF.getTypes().arrangeBuiltinFunctionCall(Context.VoidTy, args), fn, ReturnValueSlot(), args); } @@ -856,10 +854,8 @@ static void emitCPPObjectAtomicGetterCall(CodeGenFunction &CGF, llvm::Value *copyCppAtomicObjectFn = CGF.CGM.getObjCRuntime().GetCppAtomicObjectGetFunction(); - CGF.EmitCall(CGF.getTypes().arrangeFreeFunctionCall(CGF.getContext().VoidTy, - args, - FunctionType::ExtInfo(), - RequiredArgs::All), + CGF.EmitCall( + CGF.getTypes().arrangeBuiltinFunctionCall(CGF.getContext().VoidTy, args), copyCppAtomicObjectFn, ReturnValueSlot(), args); } @@ -950,8 +946,7 @@ CodeGenFunction::generateObjCGetterBody(const ObjCImplementationDecl *classImpl, // runtime already should have computed it to build the function. llvm::Instruction *CallInstruction; RValue RV = EmitCall( - getTypes().arrangeFreeFunctionCall( - propType, args, FunctionType::ExtInfo(), RequiredArgs::All), + getTypes().arrangeBuiltinFunctionCall(propType, args), getPropertyFn, ReturnValueSlot(), args, CGCalleeInfo(), &CallInstruction); if (llvm::CallInst *call = dyn_cast(CallInstruction)) @@ -1067,10 +1062,8 @@ static void emitStructSetterCall(CodeGenFunction &CGF, ObjCMethodDecl *OMD, args.add(RValue::get(CGF.Builder.getFalse()), CGF.getContext().BoolTy); llvm::Value *copyStructFn = CGF.CGM.getObjCRuntime().GetSetStructFunction(); - CGF.EmitCall(CGF.getTypes().arrangeFreeFunctionCall(CGF.getContext().VoidTy, - args, - FunctionType::ExtInfo(), - RequiredArgs::All), + CGF.EmitCall( + CGF.getTypes().arrangeBuiltinFunctionCall(CGF.getContext().VoidTy, args), copyStructFn, ReturnValueSlot(), args); } @@ -1105,10 +1098,8 @@ static void emitCPPObjectAtomicSetterCall(CodeGenFunction &CGF, llvm::Value *copyCppAtomicObjectFn = CGF.CGM.getObjCRuntime().GetCppAtomicObjectSetFunction(); - CGF.EmitCall(CGF.getTypes().arrangeFreeFunctionCall(CGF.getContext().VoidTy, - args, - FunctionType::ExtInfo(), - RequiredArgs::All), + CGF.EmitCall( + CGF.getTypes().arrangeBuiltinFunctionCall(CGF.getContext().VoidTy, args), copyCppAtomicObjectFn, ReturnValueSlot(), args); } @@ -1238,9 +1229,7 @@ CodeGenFunction::generateObjCSetterBody(const ObjCImplementationDecl *classImpl, if (setOptimizedPropertyFn) { args.add(RValue::get(arg), getContext().getObjCIdType()); args.add(RValue::get(ivarOffset), getContext().getPointerDiffType()); - EmitCall(getTypes().arrangeFreeFunctionCall(getContext().VoidTy, args, - FunctionType::ExtInfo(), - RequiredArgs::All), + EmitCall(getTypes().arrangeBuiltinFunctionCall(getContext().VoidTy, args), setOptimizedPropertyFn, ReturnValueSlot(), args); } else { args.add(RValue::get(ivarOffset), getContext().getPointerDiffType()); @@ -1251,9 +1240,7 @@ CodeGenFunction::generateObjCSetterBody(const ObjCImplementationDecl *classImpl, getContext().BoolTy); // FIXME: We shouldn't need to get the function info here, the runtime // already should have computed it to build the function. - EmitCall(getTypes().arrangeFreeFunctionCall(getContext().VoidTy, args, - FunctionType::ExtInfo(), - RequiredArgs::All), + EmitCall(getTypes().arrangeBuiltinFunctionCall(getContext().VoidTy, args), setPropertyFn, ReturnValueSlot(), args); } @@ -1610,9 +1597,8 @@ void CodeGenFunction::EmitObjCForCollectionStmt(const ObjCForCollectionStmt &S){ Args2.add(RValue::get(V), getContext().getObjCIdType()); // FIXME: We shouldn't need to get the function info here, the runtime already // should have computed it to build the function. - EmitCall(CGM.getTypes().arrangeFreeFunctionCall(getContext().VoidTy, Args2, - FunctionType::ExtInfo(), - RequiredArgs::All), + EmitCall( + CGM.getTypes().arrangeBuiltinFunctionCall(getContext().VoidTy, Args2), EnumerationMutationFn, ReturnValueSlot(), Args2); // Otherwise, or if the mutation function returns, just continue. @@ -3210,8 +3196,8 @@ CodeGenFunction::GenerateObjCAtomicSetterCopyHelperFunction( ImplicitParamDecl srcDecl(getContext(), FD, SourceLocation(), nullptr, SrcTy); args.push_back(&srcDecl); - const CGFunctionInfo &FI = CGM.getTypes().arrangeFreeFunctionDeclaration( - C.VoidTy, args, FunctionType::ExtInfo(), RequiredArgs::All); + const CGFunctionInfo &FI = + CGM.getTypes().arrangeBuiltinFunctionDeclaration(C.VoidTy, args); llvm::FunctionType *LTy = CGM.getTypes().GetFunctionType(FI); @@ -3291,8 +3277,8 @@ CodeGenFunction::GenerateObjCAtomicGetterCopyHelperFunction( ImplicitParamDecl srcDecl(getContext(), FD, SourceLocation(), nullptr, SrcTy); args.push_back(&srcDecl); - const CGFunctionInfo &FI = CGM.getTypes().arrangeFreeFunctionDeclaration( - C.VoidTy, args, FunctionType::ExtInfo(), RequiredArgs::All); + const CGFunctionInfo &FI = + CGM.getTypes().arrangeBuiltinFunctionDeclaration(C.VoidTy, args); llvm::FunctionType *LTy = CGM.getTypes().GetFunctionType(FI); diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index 09871e364b6..27cb8fad8bc 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/lib/CodeGen/CGObjCMac.cpp @@ -244,9 +244,8 @@ class ObjCCommonTypesHelper { Params.push_back(Ctx.getPointerDiffType()->getCanonicalTypeUnqualified()); Params.push_back(Ctx.BoolTy); llvm::FunctionType *FTy = - Types.GetFunctionType(Types.arrangeLLVMFunctionInfo( - IdType, false, false, Params, FunctionType::ExtInfo(), - RequiredArgs::All)); + Types.GetFunctionType( + Types.arrangeBuiltinFunctionDeclaration(IdType, Params)); return CGM.CreateRuntimeFunction(FTy, "objc_getProperty"); } @@ -264,9 +263,8 @@ class ObjCCommonTypesHelper { Params.push_back(Ctx.BoolTy); Params.push_back(Ctx.BoolTy); llvm::FunctionType *FTy = - Types.GetFunctionType(Types.arrangeLLVMFunctionInfo( - Ctx.VoidTy, false, false, Params, FunctionType::ExtInfo(), - RequiredArgs::All)); + Types.GetFunctionType( + Types.arrangeBuiltinFunctionDeclaration(Ctx.VoidTy, Params)); return CGM.CreateRuntimeFunction(FTy, "objc_setProperty"); } @@ -290,9 +288,8 @@ class ObjCCommonTypesHelper { Params.push_back(IdType); Params.push_back(Ctx.getPointerDiffType()->getCanonicalTypeUnqualified()); llvm::FunctionType *FTy = - Types.GetFunctionType(Types.arrangeLLVMFunctionInfo( - Ctx.VoidTy, false, false, Params, FunctionType::ExtInfo(), - RequiredArgs::All)); + Types.GetFunctionType( + Types.arrangeBuiltinFunctionDeclaration(Ctx.VoidTy, Params)); const char *name; if (atomic && copy) name = "objc_setProperty_atomic_copy"; @@ -317,9 +314,8 @@ class ObjCCommonTypesHelper { Params.push_back(Ctx.BoolTy); Params.push_back(Ctx.BoolTy); llvm::FunctionType *FTy = - Types.GetFunctionType(Types.arrangeLLVMFunctionInfo( - Ctx.VoidTy, false, false, Params, FunctionType::ExtInfo(), - RequiredArgs::All)); + Types.GetFunctionType( + Types.arrangeBuiltinFunctionDeclaration(Ctx.VoidTy, Params)); return CGM.CreateRuntimeFunction(FTy, "objc_copyStruct"); } @@ -336,10 +332,8 @@ class ObjCCommonTypesHelper { Params.push_back(Ctx.VoidPtrTy); Params.push_back(Ctx.VoidPtrTy); llvm::FunctionType *FTy = - Types.GetFunctionType(Types.arrangeLLVMFunctionInfo(Ctx.VoidTy, false, false, - Params, - FunctionType::ExtInfo(), - RequiredArgs::All)); + Types.GetFunctionType( + Types.arrangeBuiltinFunctionDeclaration(Ctx.VoidTy, Params)); return CGM.CreateRuntimeFunction(FTy, "objc_copyCppObjectAtomic"); } @@ -350,9 +344,8 @@ class ObjCCommonTypesHelper { SmallVector Params; Params.push_back(Ctx.getCanonicalParamType(Ctx.getObjCIdType())); llvm::FunctionType *FTy = - Types.GetFunctionType(Types.arrangeLLVMFunctionInfo( - Ctx.VoidTy, false, false, Params, FunctionType::ExtInfo(), - RequiredArgs::All)); + Types.GetFunctionType( + Types.arrangeBuiltinFunctionDeclaration(Ctx.VoidTy, Params)); return CGM.CreateRuntimeFunction(FTy, "objc_enumerationMutation"); } diff --git a/lib/CodeGen/CGObjCRuntime.cpp b/lib/CodeGen/CGObjCRuntime.cpp index 01042536a52..0caf6d9f210 100644 --- a/lib/CodeGen/CGObjCRuntime.cpp +++ b/lib/CodeGen/CGObjCRuntime.cpp @@ -363,25 +363,15 @@ CGObjCRuntime::getMessageSendInfo(const ObjCMethodDecl *method, llvm::PointerType *signatureType = CGM.getTypes().GetFunctionType(signature)->getPointerTo(); - // If that's not variadic, there's no need to recompute the ABI - // arrangement. - if (!signature.isVariadic()) - return MessageSendInfo(signature, signatureType); - - // Otherwise, there is. - FunctionType::ExtInfo einfo = signature.getExtInfo(); - const CGFunctionInfo &argsInfo = - CGM.getTypes().arrangeFreeFunctionCall(resultType, callArgs, einfo, - signature.getRequiredArgs()); - - return MessageSendInfo(argsInfo, signatureType); + const CGFunctionInfo &signatureForCall = + CGM.getTypes().arrangeCall(signature, callArgs); + + return MessageSendInfo(signatureForCall, signatureType); } // There's no method; just use a default CC. const CGFunctionInfo &argsInfo = - CGM.getTypes().arrangeFreeFunctionCall(resultType, callArgs, - FunctionType::ExtInfo(), - RequiredArgs::All); + CGM.getTypes().arrangeUnprototypedObjCMessageSend(resultType, callArgs); // Derive the signature to call from that. llvm::PointerType *signatureType = diff --git a/lib/CodeGen/CGOpenMPRuntime.cpp b/lib/CodeGen/CGOpenMPRuntime.cpp index 246c553bdad..fbf4d351ac3 100644 --- a/lib/CodeGen/CGOpenMPRuntime.cpp +++ b/lib/CodeGen/CGOpenMPRuntime.cpp @@ -329,6 +329,7 @@ void CGOpenMPRuntime::clear() { InternalVars.clear(); } + // Layout information for ident_t. static CharUnits getIdentAlign(CodeGenModule &CGM) { return CGM.getPointerAlign(); @@ -1144,9 +1145,8 @@ llvm::Function *CGOpenMPRuntime::emitThreadPrivateVarDefinition( /*Id=*/nullptr, CGM.getContext().VoidPtrTy); Args.push_back(&Dst); - auto &FI = CGM.getTypes().arrangeFreeFunctionDeclaration( - CGM.getContext().VoidPtrTy, Args, FunctionType::ExtInfo(), - /*isVariadic=*/false); + auto &FI = CGM.getTypes().arrangeBuiltinFunctionDeclaration( + CGM.getContext().VoidPtrTy, Args); auto FTy = CGM.getTypes().GetFunctionType(FI); auto Fn = CGM.CreateGlobalInitOrDestructFunction( FTy, ".__kmpc_global_ctor_.", FI, Loc); @@ -1176,9 +1176,8 @@ llvm::Function *CGOpenMPRuntime::emitThreadPrivateVarDefinition( /*Id=*/nullptr, CGM.getContext().VoidPtrTy); Args.push_back(&Dst); - auto &FI = CGM.getTypes().arrangeFreeFunctionDeclaration( - CGM.getContext().VoidTy, Args, FunctionType::ExtInfo(), - /*isVariadic=*/false); + auto &FI = CGM.getTypes().arrangeBuiltinFunctionDeclaration( + CGM.getContext().VoidTy, Args); auto FTy = CGM.getTypes().GetFunctionType(FI); auto Fn = CGM.CreateGlobalInitOrDestructFunction( FTy, ".__kmpc_global_dtor_.", FI, Loc); @@ -1549,9 +1548,7 @@ static llvm::Value *emitCopyprivateCopyFunction( C.VoidPtrTy); Args.push_back(&LHSArg); Args.push_back(&RHSArg); - FunctionType::ExtInfo EI; - auto &CGFI = CGM.getTypes().arrangeFreeFunctionDeclaration( - C.VoidTy, Args, EI, /*isVariadic=*/false); + auto &CGFI = CGM.getTypes().arrangeBuiltinFunctionDeclaration(C.VoidTy, Args); auto *Fn = llvm::Function::Create( CGM.getTypes().GetFunctionType(CGFI), llvm::GlobalValue::InternalLinkage, ".omp.copyprivate.copy_func", &CGM.getModule()); @@ -2092,9 +2089,7 @@ createOffloadingBinaryDescriptorFunction(CodeGenModule &CGM, StringRef Name, CodeGenFunction CGF(CGM); GlobalDecl(); - auto &FI = CGM.getTypes().arrangeFreeFunctionDeclaration( - C.VoidTy, Args, FunctionType::ExtInfo(), - /*isVariadic=*/false); + auto &FI = CGM.getTypes().arrangeBuiltinFunctionDeclaration(C.VoidTy, Args); auto FTy = CGM.getTypes().GetFunctionType(FI); auto *Fn = CGM.CreateGlobalInitOrDestructFunction(FTy, Name, FI, SourceLocation()); @@ -2574,10 +2569,8 @@ emitProxyTaskFunction(CodeGenModule &CGM, SourceLocation Loc, KmpTaskTWithPrivatesPtrQTy.withRestrict()); Args.push_back(&GtidArg); Args.push_back(&TaskTypeArg); - FunctionType::ExtInfo Info; auto &TaskEntryFnInfo = - CGM.getTypes().arrangeFreeFunctionDeclaration(KmpInt32Ty, Args, Info, - /*isVariadic=*/false); + CGM.getTypes().arrangeBuiltinFunctionDeclaration(KmpInt32Ty, Args); auto *TaskEntryTy = CGM.getTypes().GetFunctionType(TaskEntryFnInfo); auto *TaskEntry = llvm::Function::Create(TaskEntryTy, llvm::GlobalValue::InternalLinkage, @@ -2643,8 +2636,7 @@ static llvm::Value *emitDestructorsFunction(CodeGenModule &CGM, Args.push_back(&TaskTypeArg); FunctionType::ExtInfo Info; auto &DestructorFnInfo = - CGM.getTypes().arrangeFreeFunctionDeclaration(KmpInt32Ty, Args, Info, - /*isVariadic=*/false); + CGM.getTypes().arrangeBuiltinFunctionDeclaration(KmpInt32Ty, Args); auto *DestructorFnTy = CGM.getTypes().GetFunctionType(DestructorFnInfo); auto *DestructorFn = llvm::Function::Create(DestructorFnTy, llvm::GlobalValue::InternalLinkage, @@ -2717,10 +2709,8 @@ emitTaskPrivateMappingFunction(CodeGenModule &CGM, SourceLocation Loc, PrivateVarsPos[VD] = Counter; ++Counter; } - FunctionType::ExtInfo Info; auto &TaskPrivatesMapFnInfo = - CGM.getTypes().arrangeFreeFunctionDeclaration(C.VoidTy, Args, Info, - /*isVariadic=*/false); + CGM.getTypes().arrangeBuiltinFunctionDeclaration(C.VoidTy, Args); auto *TaskPrivatesMapTy = CGM.getTypes().GetFunctionType(TaskPrivatesMapFnInfo); auto *TaskPrivatesMap = llvm::Function::Create( @@ -3225,9 +3215,7 @@ static llvm::Value *emitReductionFunction(CodeGenModule &CGM, C.VoidPtrTy); Args.push_back(&LHSArg); Args.push_back(&RHSArg); - FunctionType::ExtInfo EI; - auto &CGFI = CGM.getTypes().arrangeFreeFunctionDeclaration( - C.VoidTy, Args, EI, /*isVariadic=*/false); + auto &CGFI = CGM.getTypes().arrangeBuiltinFunctionDeclaration(C.VoidTy, Args); auto *Fn = llvm::Function::Create( CGM.getTypes().GetFunctionType(CGFI), llvm::GlobalValue::InternalLinkage, ".omp.reduction.reduction_func", &CGM.getModule()); diff --git a/lib/CodeGen/CGStmt.cpp b/lib/CodeGen/CGStmt.cpp index cc4fa2ec597..a1e44c807e5 100644 --- a/lib/CodeGen/CGStmt.cpp +++ b/lib/CodeGen/CGStmt.cpp @@ -2147,8 +2147,7 @@ CodeGenFunction::GenerateCapturedStmtFunction(const CapturedStmt &S) { // Create the function declaration. FunctionType::ExtInfo ExtInfo; const CGFunctionInfo &FuncInfo = - CGM.getTypes().arrangeFreeFunctionDeclaration(Ctx.VoidTy, Args, ExtInfo, - /*IsVariadic=*/false); + CGM.getTypes().arrangeBuiltinFunctionDeclaration(Ctx.VoidTy, Args); llvm::FunctionType *FuncLLVMTy = CGM.getTypes().GetFunctionType(FuncInfo); llvm::Function *F = diff --git a/lib/CodeGen/CGStmtOpenMP.cpp b/lib/CodeGen/CGStmtOpenMP.cpp index bf00b04563c..68bd68bbf12 100644 --- a/lib/CodeGen/CGStmtOpenMP.cpp +++ b/lib/CodeGen/CGStmtOpenMP.cpp @@ -120,8 +120,7 @@ CodeGenFunction::GenerateOpenMPCapturedStmtFunction(const CapturedStmt &S) { // Create the function declaration. FunctionType::ExtInfo ExtInfo; const CGFunctionInfo &FuncInfo = - CGM.getTypes().arrangeFreeFunctionDeclaration(Ctx.VoidTy, Args, ExtInfo, - /*IsVariadic=*/false); + CGM.getTypes().arrangeBuiltinFunctionDeclaration(Ctx.VoidTy, Args); llvm::FunctionType *FuncLLVMTy = CGM.getTypes().GetFunctionType(FuncInfo); llvm::Function *F = llvm::Function::Create( diff --git a/lib/CodeGen/CodeGenABITypes.cpp b/lib/CodeGen/CodeGenABITypes.cpp index 643c996e2ec..16a7db47c80 100644 --- a/lib/CodeGen/CodeGenABITypes.cpp +++ b/lib/CodeGen/CodeGenABITypes.cpp @@ -66,5 +66,5 @@ const CGFunctionInfo &CodeGenABITypes::arrangeFreeFunctionCall( FunctionType::ExtInfo info, RequiredArgs args) { return CGM->getTypes().arrangeLLVMFunctionInfo( returnType, /*IsInstanceMethod=*/false, /*IsChainCall=*/false, argTypes, - info, args); + info, {}, args); } diff --git a/lib/CodeGen/CodeGenTypes.h b/lib/CodeGen/CodeGenTypes.h index a96f23c4489..477ea602bd3 100644 --- a/lib/CodeGen/CodeGenTypes.h +++ b/lib/CodeGen/CodeGenTypes.h @@ -214,6 +214,10 @@ class CodeGenTypes { /// replace the 'opaque' type we previously made for it if applicable. void UpdateCompletedType(const TagDecl *TD); + /// \brief Remove stale types from the type cache when an inheritance model + /// gets assigned to a class. + void RefreshTypeCacheForClass(const CXXRecordDecl *RD); + /// getNullaryFunctionInfo - Get the function info for a void() /// function with standard CC. const CGFunctionInfo &arrangeNullaryFunction(); @@ -239,16 +243,55 @@ class CodeGenTypes { // this for compatibility reasons. const CGFunctionInfo &arrangeGlobalDeclaration(GlobalDecl GD); + + /// Given a function info for a declaration, return the function info + /// for a call with the given arguments. + /// + /// Often this will be able to simply return the declaration info. + const CGFunctionInfo &arrangeCall(const CGFunctionInfo &declFI, + const CallArgList &args); + + /// Free functions are functions that are compatible with an ordinary + /// C function pointer type. const CGFunctionInfo &arrangeFunctionDeclaration(const FunctionDecl *FD); + const CGFunctionInfo &arrangeFreeFunctionCall(const CallArgList &Args, + const FunctionType *Ty, + bool ChainCall); + const CGFunctionInfo &arrangeFreeFunctionType(CanQual Ty, + const FunctionDecl *FD); + const CGFunctionInfo &arrangeFreeFunctionType(CanQual Ty); + + /// A nullary function is a freestanding function of type 'void ()'. + /// This method works for both calls and declarations. + const CGFunctionInfo &arrangeNullaryFunction(); + + /// A builtin function is a freestanding function using the default + /// C conventions. const CGFunctionInfo & - arrangeFreeFunctionDeclaration(QualType ResTy, const FunctionArgList &Args, - const FunctionType::ExtInfo &Info, - bool isVariadic); + arrangeBuiltinFunctionDeclaration(QualType resultType, + const FunctionArgList &args); + const CGFunctionInfo & + arrangeBuiltinFunctionDeclaration(CanQualType resultType, + ArrayRef argTypes); + const CGFunctionInfo &arrangeBuiltinFunctionCall(QualType resultType, + const CallArgList &args); + /// Objective-C methods are C functions with some implicit parameters. const CGFunctionInfo &arrangeObjCMethodDeclaration(const ObjCMethodDecl *MD); const CGFunctionInfo &arrangeObjCMessageSendSignature(const ObjCMethodDecl *MD, QualType receiverType); + const CGFunctionInfo &arrangeUnprototypedObjCMessageSend( + QualType returnType, + const CallArgList &args); + + /// Block invocation functions are C functions with an implicit parameter. + const CGFunctionInfo &arrangeBlockFunctionDeclaration( + const FunctionProtoType *type, + const FunctionArgList &args); + const CGFunctionInfo &arrangeBlockFunctionCall(const CallArgList &args, + const FunctionType *type); + /// C++ methods have some special rules and also have implicit parameters. const CGFunctionInfo &arrangeCXXMethodDeclaration(const CXXMethodDecl *MD); const CGFunctionInfo &arrangeCXXStructorDeclaration(const CXXMethodDecl *MD, StructorType Type); @@ -256,15 +299,6 @@ class CodeGenTypes { const CXXConstructorDecl *D, CXXCtorType CtorKind, unsigned ExtraArgs); - const CGFunctionInfo &arrangeFreeFunctionCall(const CallArgList &Args, - const FunctionType *Ty, - bool ChainCall); - const CGFunctionInfo &arrangeFreeFunctionCall(QualType ResTy, - const CallArgList &args, - FunctionType::ExtInfo info, - RequiredArgs required); - const CGFunctionInfo &arrangeBlockFunctionCall(const CallArgList &args, - const FunctionType *type); const CGFunctionInfo &arrangeCXXMethodCall(const CallArgList &args, const FunctionProtoType *type, @@ -272,9 +306,6 @@ class CodeGenTypes { const CGFunctionInfo &arrangeMSMemberPointerThunk(const CXXMethodDecl *MD); const CGFunctionInfo &arrangeMSCtorClosure(const CXXConstructorDecl *CD, CXXCtorType CT); - const CGFunctionInfo &arrangeFreeFunctionType(CanQual Ty, - const FunctionDecl *FD); - const CGFunctionInfo &arrangeFreeFunctionType(CanQual Ty); const CGFunctionInfo &arrangeCXXMethodType(const CXXRecordDecl *RD, const FunctionProtoType *FTP, const CXXMethodDecl *MD); @@ -290,6 +321,7 @@ class CodeGenTypes { bool chainCall, ArrayRef argTypes, FunctionType::ExtInfo info, + ArrayRef paramInfos, RequiredArgs args); /// \brief Compute a new LLVM record layout object for the given record. diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp index 84f43dd3a8e..e016b99bbb2 100644 --- a/lib/CodeGen/ItaniumCXXABI.cpp +++ b/lib/CodeGen/ItaniumCXXABI.cpp @@ -2180,9 +2180,8 @@ ItaniumCXXABI::getOrCreateThreadLocalWrapper(const VarDecl *VD, if (RetQT->isReferenceType()) RetQT = RetQT.getNonReferenceType(); - const CGFunctionInfo &FI = CGM.getTypes().arrangeFreeFunctionDeclaration( - getContext().getPointerType(RetQT), FunctionArgList(), - FunctionType::ExtInfo(), false); + const CGFunctionInfo &FI = CGM.getTypes().arrangeBuiltinFunctionDeclaration( + getContext().getPointerType(RetQT), FunctionArgList()); llvm::FunctionType *FnTy = CGM.getTypes().GetFunctionType(FI); llvm::Function *Wrapper = @@ -2274,9 +2273,7 @@ void ItaniumCXXABI::EmitThreadLocalInitFuncs( Init = llvm::Function::Create( FnTy, llvm::GlobalVariable::ExternalWeakLinkage, InitFnName.str(), &CGM.getModule()); - const CGFunctionInfo &FI = CGM.getTypes().arrangeFreeFunctionDeclaration( - CGM.getContext().VoidTy, FunctionArgList(), FunctionType::ExtInfo(), - false); + const CGFunctionInfo &FI = CGM.getTypes().arrangeNullaryFunction(); CGM.SetLLVMFunctionAttributes(nullptr, FI, cast(Init)); } From e71ce75308e01fc8f55a8c2795fddd1b3f3ff84e Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 11 Mar 2016 04:30:43 +0000 Subject: [PATCH 464/742] Add a coerce-and-expand ABIArgInfo as a generalization of some of the things we do with Expand / Direct. NFC for now, but this will be used by swiftcall expansion. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263192 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/CodeGen/CGFunctionInfo.h | 121 ++++++++++++++++++++--- lib/CodeGen/CGBuilder.h | 7 ++ lib/CodeGen/CGCall.cpp | 127 ++++++++++++++++++++++++- lib/CodeGen/TargetInfo.cpp | 7 ++ 4 files changed, 243 insertions(+), 19 deletions(-) diff --git a/include/clang/CodeGen/CGFunctionInfo.h b/include/clang/CodeGen/CGFunctionInfo.h index 9af28975bdd..5066c5dfda3 100644 --- a/include/clang/CodeGen/CGFunctionInfo.h +++ b/include/clang/CodeGen/CGFunctionInfo.h @@ -19,14 +19,10 @@ #include "clang/AST/CanonicalType.h" #include "clang/AST/CharUnits.h" #include "clang/AST/Type.h" +#include "llvm/IR/DerivedTypes.h" #include "llvm/ADT/FoldingSet.h" #include -namespace llvm { - class Type; - class StructType; -} - namespace clang { class Decl; @@ -63,6 +59,12 @@ class ABIArgInfo { /// are all scalar types or are themselves expandable types. Expand, + /// CoerceAndExpand - Only valid for aggregate argument types. The + /// structure should be expanded into consecutive arguments corresponding + /// to the non-array elements of the type stored in CoerceToType. + /// Array elements in the type are assumed to be padding and skipped. + CoerceAndExpand, + /// InAlloca - Pass the argument directly using the LLVM inalloca attribute. /// This is similar to indirect with byval, except it only applies to /// arguments stored in memory and forbids any implicit copies. When @@ -74,8 +76,11 @@ class ABIArgInfo { }; private: - llvm::Type *TypeData; // isDirect() || isExtend() - llvm::Type *PaddingType; + llvm::Type *TypeData; // canHaveCoerceToType() + union { + llvm::Type *PaddingType; // canHavePaddingType() + llvm::Type *UnpaddedCoerceAndExpandType; // isCoerceAndExpand() + }; union { unsigned DirectOffset; // isDirect() || isExtend() unsigned IndirectAlign; // isIndirect() @@ -90,8 +95,22 @@ class ABIArgInfo { bool InReg : 1; // isDirect() || isExtend() || isIndirect() bool CanBeFlattened: 1; // isDirect() + bool canHavePaddingType() const { + return isDirect() || isExtend() || isIndirect() || isExpand(); + } + void setPaddingType(llvm::Type *T) { + assert(canHavePaddingType()); + PaddingType = T; + } + + void setUnpaddedCoerceToType(llvm::Type *T) { + assert(isCoerceAndExpand()); + UnpaddedCoerceAndExpandType = T; + } + ABIArgInfo(Kind K) - : PaddingType(nullptr), TheKind(K), PaddingInReg(false), InReg(false) {} + : TheKind(K), PaddingInReg(false), InReg(false) { + } public: ABIArgInfo() @@ -103,8 +122,8 @@ class ABIArgInfo { bool CanBeFlattened = true) { auto AI = ABIArgInfo(Direct); AI.setCoerceToType(T); - AI.setDirectOffset(Offset); AI.setPaddingType(Padding); + AI.setDirectOffset(Offset); AI.setCanBeFlattened(CanBeFlattened); return AI; } @@ -116,6 +135,7 @@ class ABIArgInfo { static ABIArgInfo getExtend(llvm::Type *T = nullptr) { auto AI = ABIArgInfo(Extend); AI.setCoerceToType(T); + AI.setPaddingType(nullptr); AI.setDirectOffset(0); return AI; } @@ -150,7 +170,9 @@ class ABIArgInfo { return AI; } static ABIArgInfo getExpand() { - return ABIArgInfo(Expand); + auto AI = ABIArgInfo(Expand); + AI.setPaddingType(nullptr); + return AI; } static ABIArgInfo getExpandWithPadding(bool PaddingInReg, llvm::Type *Padding) { @@ -160,6 +182,54 @@ class ABIArgInfo { return AI; } + /// \param unpaddedCoerceToType The coerce-to type with padding elements + /// removed, canonicalized to a single element if it would otherwise + /// have exactly one element. + static ABIArgInfo getCoerceAndExpand(llvm::StructType *coerceToType, + llvm::Type *unpaddedCoerceToType) { +#ifndef NDEBUG + // Sanity checks on unpaddedCoerceToType. + + // Assert that we only have a struct type if there are multiple elements. + auto unpaddedStruct = dyn_cast(unpaddedCoerceToType); + assert(!unpaddedStruct || unpaddedStruct->getNumElements() != 1); + + // Assert that all the non-padding elements have a corresponding element + // in the unpadded type. + unsigned unpaddedIndex = 0; + for (auto eltType : coerceToType->elements()) { + if (isPaddingForCoerceAndExpand(eltType)) continue; + if (unpaddedStruct) { + assert(unpaddedStruct->getElementType(unpaddedIndex) == eltType); + } else { + assert(unpaddedIndex == 0 && unpaddedCoerceToType == eltType); + } + unpaddedIndex++; + } + + // Assert that there aren't extra elements in the unpadded type. + if (unpaddedStruct) { + assert(unpaddedStruct->getNumElements() == unpaddedIndex); + } else { + assert(unpaddedIndex == 1); + } +#endif + + auto AI = ABIArgInfo(CoerceAndExpand); + AI.setCoerceToType(coerceToType); + AI.setUnpaddedCoerceToType(unpaddedCoerceToType); + return AI; + } + + static bool isPaddingForCoerceAndExpand(llvm::Type *eltType) { + if (eltType->isArrayTy()) { + assert(eltType->getArrayElementType()->isIntegerTy(8)); + return true; + } else { + return false; + } + } + Kind getKind() const { return TheKind; } bool isDirect() const { return TheKind == Direct; } bool isInAlloca() const { return TheKind == InAlloca; } @@ -167,8 +237,11 @@ class ABIArgInfo { bool isIgnore() const { return TheKind == Ignore; } bool isIndirect() const { return TheKind == Indirect; } bool isExpand() const { return TheKind == Expand; } + bool isCoerceAndExpand() const { return TheKind == CoerceAndExpand; } - bool canHaveCoerceToType() const { return isDirect() || isExtend(); } + bool canHaveCoerceToType() const { + return isDirect() || isExtend() || isCoerceAndExpand(); + } // Direct/Extend accessors unsigned getDirectOffset() const { @@ -180,9 +253,9 @@ class ABIArgInfo { DirectOffset = Offset; } - llvm::Type *getPaddingType() const { return PaddingType; } - - void setPaddingType(llvm::Type *T) { PaddingType = T; } + llvm::Type *getPaddingType() const { + return (canHavePaddingType() ? PaddingType : nullptr); + } bool getPaddingInReg() const { return PaddingInReg; @@ -201,6 +274,26 @@ class ABIArgInfo { TypeData = T; } + llvm::StructType *getCoerceAndExpandType() const { + assert(isCoerceAndExpand()); + return cast(TypeData); + } + + llvm::Type *getUnpaddedCoerceAndExpandType() const { + assert(isCoerceAndExpand()); + return UnpaddedCoerceAndExpandType; + } + + ArrayRefgetCoerceAndExpandTypeSequence() const { + assert(isCoerceAndExpand()); + if (auto structTy = + dyn_cast(UnpaddedCoerceAndExpandType)) { + return structTy->elements(); + } else { + return llvm::makeArrayRef(&UnpaddedCoerceAndExpandType, 1); + } + } + bool getInReg() const { assert((isDirect() || isExtend() || isIndirect()) && "Invalid kind!"); return InReg; diff --git a/lib/CodeGen/CGBuilder.h b/lib/CodeGen/CGBuilder.h index d6fc1488677..027435d7c59 100644 --- a/lib/CodeGen/CGBuilder.h +++ b/lib/CodeGen/CGBuilder.h @@ -10,6 +10,7 @@ #ifndef LLVM_CLANG_LIB_CODEGEN_CGBUILDER_H #define LLVM_CLANG_LIB_CODEGEN_CGBUILDER_H +#include "llvm/IR/DataLayout.h" #include "llvm/IR/IRBuilder.h" #include "Address.h" #include "CodeGenTypeCache.h" @@ -185,6 +186,12 @@ class CGBuilderTy : public CGBuilderBaseTy { Addr.getPointer(), Index, Name), Addr.getAlignment().alignmentAtOffset(Offset)); } + Address CreateStructGEP(Address Addr, unsigned Index, + const llvm::StructLayout *Layout, + const llvm::Twine &Name = "") { + auto Offset = CharUnits::fromQuantity(Layout->getElementOffset(Index)); + return CreateStructGEP(Addr, Index, Offset, Name); + } /// Given /// %addr = [n x T]* ... diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index a4f2c1a3400..ebdcd705bee 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -1364,11 +1364,13 @@ void ClangToLLVMArgMapping::construct(const ASTContext &Context, // ignore and inalloca doesn't have matching LLVM parameters. IRArgs.NumberOfArgs = 0; break; - case ABIArgInfo::Expand: { + case ABIArgInfo::CoerceAndExpand: + IRArgs.NumberOfArgs = AI.getCoerceAndExpandTypeSequence().size(); + break; + case ABIArgInfo::Expand: IRArgs.NumberOfArgs = getExpansionSize(ArgType, Context); break; } - } if (IRArgs.NumberOfArgs > 0) { IRArgs.FirstArgIndex = IRArgNo; @@ -1467,6 +1469,10 @@ CodeGenTypes::GetFunctionType(const CGFunctionInfo &FI) { case ABIArgInfo::Ignore: resultType = llvm::Type::getVoidTy(getLLVMContext()); break; + + case ABIArgInfo::CoerceAndExpand: + resultType = retAI.getUnpaddedCoerceAndExpandType(); + break; } ClangToLLVMArgMapping IRFunctionArgs(getContext(), FI, true); @@ -1534,6 +1540,15 @@ CodeGenTypes::GetFunctionType(const CGFunctionInfo &FI) { break; } + case ABIArgInfo::CoerceAndExpand: { + auto ArgTypesIter = ArgTypes.begin() + FirstIRArg; + for (auto EltTy : ArgInfo.getCoerceAndExpandTypeSequence()) { + *ArgTypesIter++ = EltTy; + } + assert(ArgTypesIter == ArgTypes.begin() + FirstIRArg + NumIRArgs); + break; + } + case ABIArgInfo::Expand: auto ArgTypesIter = ArgTypes.begin() + FirstIRArg; getExpandedTypes(it->type, ArgTypesIter); @@ -1764,6 +1779,9 @@ void CodeGenModule::ConstructAttributeList( break; } + case ABIArgInfo::CoerceAndExpand: + break; + case ABIArgInfo::Expand: llvm_unreachable("Invalid ABI kind for return argument"); } @@ -1871,7 +1889,8 @@ void CodeGenModule::ConstructAttributeList( } case ABIArgInfo::Ignore: case ABIArgInfo::Expand: - continue; + case ABIArgInfo::CoerceAndExpand: + break; case ABIArgInfo::InAlloca: // inalloca disables readnone and readonly. @@ -2244,6 +2263,29 @@ void CodeGenFunction::EmitFunctionProlog(const CGFunctionInfo &FI, break; } + case ABIArgInfo::CoerceAndExpand: { + // Reconstruct into a temporary. + Address alloca = CreateMemTemp(Ty, getContext().getDeclAlign(Arg)); + ArgVals.push_back(ParamValue::forIndirect(alloca)); + + auto coercionType = ArgI.getCoerceAndExpandType(); + alloca = Builder.CreateElementBitCast(alloca, coercionType); + auto layout = CGM.getDataLayout().getStructLayout(coercionType); + + unsigned argIndex = FirstIRArg; + for (unsigned i = 0, e = coercionType->getNumElements(); i != e; ++i) { + llvm::Type *eltType = coercionType->getElementType(i); + if (ABIArgInfo::isPaddingForCoerceAndExpand(eltType)) + continue; + + auto eltAddr = Builder.CreateStructGEP(alloca, i, layout); + auto elt = FnArgs[argIndex++]; + Builder.CreateStore(elt, eltAddr); + } + assert(argIndex == FirstIRArg + NumIRArgs); + break; + } + case ABIArgInfo::Expand: { // If this structure was expanded into multiple arguments then // we need to create a temporary and reconstruct it from the @@ -2634,6 +2676,40 @@ void CodeGenFunction::EmitFunctionEpilog(const CGFunctionInfo &FI, case ABIArgInfo::Ignore: break; + case ABIArgInfo::CoerceAndExpand: { + auto coercionType = RetAI.getCoerceAndExpandType(); + auto layout = CGM.getDataLayout().getStructLayout(coercionType); + + // Load all of the coerced elements out into results. + llvm::SmallVector results; + Address addr = Builder.CreateElementBitCast(ReturnValue, coercionType); + for (unsigned i = 0, e = coercionType->getNumElements(); i != e; ++i) { + auto coercedEltType = coercionType->getElementType(i); + if (ABIArgInfo::isPaddingForCoerceAndExpand(coercedEltType)) + continue; + + auto eltAddr = Builder.CreateStructGEP(addr, i, layout); + auto elt = Builder.CreateLoad(eltAddr); + results.push_back(elt); + } + + // If we have one result, it's the single direct result type. + if (results.size() == 1) { + RV = results[0]; + + // Otherwise, we need to make a first-class aggregate. + } else { + // Construct a return type that lacks padding elements. + llvm::Type *returnType = RetAI.getUnpaddedCoerceAndExpandType(); + + RV = llvm::UndefValue::get(returnType); + for (unsigned i = 0, e = results.size(); i != e; ++i) { + RV = Builder.CreateInsertValue(RV, results[i], i); + } + } + break; + } + case ABIArgInfo::Expand: llvm_unreachable("Invalid ABI kind for return argument"); } @@ -3367,7 +3443,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, // alloca to hold the result, unless one is given to us. Address SRetPtr = Address::invalid(); size_t UnusedReturnSize = 0; - if (RetAI.isIndirect() || RetAI.isInAlloca()) { + if (RetAI.isIndirect() || RetAI.isInAlloca() || RetAI.isCoerceAndExpand()) { if (!ReturnValue.isNull()) { SRetPtr = ReturnValue.getValue(); } else { @@ -3381,7 +3457,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, } if (IRFunctionArgs.hasSRetArg()) { IRCallArgs[IRFunctionArgs.getSRetArgNo()] = SRetPtr.getPointer(); - } else { + } else if (RetAI.isInAlloca()) { Address Addr = createInAllocaStructGEP(RetAI.getInAllocaFieldIndex()); Builder.CreateStore(SRetPtr.getPointer(), Addr); } @@ -3561,6 +3637,29 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, break; } + case ABIArgInfo::CoerceAndExpand: { + assert(RV.isAggregate() && + "CoerceAndExpand does not support non-aggregate types yet"); + + auto coercionType = ArgInfo.getCoerceAndExpandType(); + auto layout = CGM.getDataLayout().getStructLayout(coercionType); + + Address addr = RV.getAggregateAddress(); + addr = Builder.CreateElementBitCast(addr, coercionType); + + unsigned IRArgPos = FirstIRArg; + for (unsigned i = 0, e = coercionType->getNumElements(); i != e; ++i) { + llvm::Type *eltType = coercionType->getElementType(i); + if (ABIArgInfo::isPaddingForCoerceAndExpand(eltType)) continue; + Address eltAddr = Builder.CreateStructGEP(addr, i, layout); + llvm::Value *elt = Builder.CreateLoad(eltAddr); + IRCallArgs[IRArgPos++] = elt; + } + assert(IRArgPos == FirstIRArg + NumIRArgs); + + break; + } + case ABIArgInfo::Expand: unsigned IRArgPos = FirstIRArg; ExpandTypeToArgs(I->Ty, RV, IRFuncTy, IRCallArgs, IRArgPos); @@ -3755,6 +3854,24 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, return ret; } + case ABIArgInfo::CoerceAndExpand: { + auto coercionType = RetAI.getCoerceAndExpandType(); + auto layout = CGM.getDataLayout().getStructLayout(coercionType); + + Address addr = SRetPtr; + addr = Builder.CreateElementBitCast(addr, coercionType); + + unsigned unpaddedIndex = 0; + for (unsigned i = 0, e = coercionType->getNumElements(); i != e; ++i) { + llvm::Type *eltType = coercionType->getElementType(i); + if (ABIArgInfo::isPaddingForCoerceAndExpand(eltType)) continue; + Address eltAddr = Builder.CreateStructGEP(addr, i, layout); + llvm::Value *elt = Builder.CreateExtractValue(CI, unpaddedIndex++); + Builder.CreateStore(elt, eltAddr); + } + break; + } + case ABIArgInfo::Ignore: // If we are ignoring an argument that had a result, make sure to // construct the appropriate return value for our caller. diff --git a/lib/CodeGen/TargetInfo.cpp b/lib/CodeGen/TargetInfo.cpp index e0ed00f753e..73c146ee850 100644 --- a/lib/CodeGen/TargetInfo.cpp +++ b/lib/CodeGen/TargetInfo.cpp @@ -158,6 +158,10 @@ LLVM_DUMP_METHOD void ABIArgInfo::dump() const { case Expand: OS << "Expand"; break; + case CoerceAndExpand: + OS << "CoerceAndExpand Type="; + getCoerceAndExpandType()->print(OS); + break; } OS << ")\n"; } @@ -1501,6 +1505,7 @@ static bool isArgInAlloca(const ABIArgInfo &Info) { case ABIArgInfo::Direct: case ABIArgInfo::Extend: case ABIArgInfo::Expand: + case ABIArgInfo::CoerceAndExpand: if (Info.getInReg()) return false; return true; @@ -6754,6 +6759,7 @@ Address SparcV9ABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, CharUnits Stride; switch (AI.getKind()) { case ABIArgInfo::Expand: + case ABIArgInfo::CoerceAndExpand: case ABIArgInfo::InAlloca: llvm_unreachable("Unsupported ABI kind for va_arg"); @@ -6982,6 +6988,7 @@ Address XCoreABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, CharUnits ArgSize = CharUnits::Zero(); switch (AI.getKind()) { case ABIArgInfo::Expand: + case ABIArgInfo::CoerceAndExpand: case ABIArgInfo::InAlloca: llvm_unreachable("Unsupported ABI kind for va_arg"); case ABIArgInfo::Ignore: From 96bd6983ceff5f3d107254cda477070228d777f9 Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 11 Mar 2016 04:55:21 +0000 Subject: [PATCH 465/742] Speculatively attempt to fix the MSVC build by making some methods non-private. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263193 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/CodeGen/CGFunctionInfo.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/clang/CodeGen/CGFunctionInfo.h b/include/clang/CodeGen/CGFunctionInfo.h index 5066c5dfda3..b8f67d52183 100644 --- a/include/clang/CodeGen/CGFunctionInfo.h +++ b/include/clang/CodeGen/CGFunctionInfo.h @@ -496,6 +496,15 @@ class CGFunctionInfo : public llvm::FoldingSetNode { ArrayRef argTypes, RequiredArgs required); + // Friending class TrailingObjects is apparently not good enough for MSVC, + // so these have to be public. + size_t numTrailingObjects(OverloadToken) const { + return NumArgs + 1; + } + size_t numTrailingObjects(OverloadToken) const { + return (HasExtParameterInfos ? NumArgs : 0); + } + typedef const ArgInfo *const_arg_iterator; typedef ArgInfo *arg_iterator; From 75d7bf981642d732b55194f768d74152bfad2dce Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 11 Mar 2016 05:03:01 +0000 Subject: [PATCH 466/742] Removing the friend declaration was not a good idea. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263194 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/CodeGen/CGFunctionInfo.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/clang/CodeGen/CGFunctionInfo.h b/include/clang/CodeGen/CGFunctionInfo.h index b8f67d52183..decc8073ca5 100644 --- a/include/clang/CodeGen/CGFunctionInfo.h +++ b/include/clang/CodeGen/CGFunctionInfo.h @@ -498,6 +498,7 @@ class CGFunctionInfo : public llvm::FoldingSetNode { // Friending class TrailingObjects is apparently not good enough for MSVC, // so these have to be public. + friend class TrailingObjects; size_t numTrailingObjects(OverloadToken) const { return NumArgs + 1; } From 6c00301c2d6ba7733a6684724ea920eda8c590d3 Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 4 Apr 2016 13:10:14 -0700 Subject: [PATCH 467/742] Fix cherry-pick for this branch. --- include/clang/CodeGen/CGFunctionInfo.h | 10 ---------- lib/CodeGen/CGObjCMac.cpp | 7 +++---- lib/CodeGen/CodeGenTypes.h | 4 ---- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/include/clang/CodeGen/CGFunctionInfo.h b/include/clang/CodeGen/CGFunctionInfo.h index decc8073ca5..5066c5dfda3 100644 --- a/include/clang/CodeGen/CGFunctionInfo.h +++ b/include/clang/CodeGen/CGFunctionInfo.h @@ -496,16 +496,6 @@ class CGFunctionInfo : public llvm::FoldingSetNode { ArrayRef argTypes, RequiredArgs required); - // Friending class TrailingObjects is apparently not good enough for MSVC, - // so these have to be public. - friend class TrailingObjects; - size_t numTrailingObjects(OverloadToken) const { - return NumArgs + 1; - } - size_t numTrailingObjects(OverloadToken) const { - return (HasExtParameterInfos ? NumArgs : 0); - } - typedef const ArgInfo *const_arg_iterator; typedef ArgInfo *arg_iterator; diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index 27cb8fad8bc..9c681988510 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/lib/CodeGen/CGObjCMac.cpp @@ -357,11 +357,10 @@ class ObjCCommonTypesHelper { Params.push_back( Ctx.getCanonicalType(Ctx.getPointerType(Ctx.CharTy.withConst()))); llvm::FunctionType *FTy = - Types.GetFunctionType(Types.arrangeLLVMFunctionInfo( + Types.GetFunctionType( + Types.arrangeBuiltinFunctionDeclaration( Ctx.getCanonicalType(Ctx.getObjCClassType()), - false, false, Params, - FunctionType::ExtInfo(), - RequiredArgs::All)); + Params)); return CGM.CreateRuntimeFunction(FTy, "objc_lookUpClass"); } diff --git a/lib/CodeGen/CodeGenTypes.h b/lib/CodeGen/CodeGenTypes.h index 477ea602bd3..34f6b611365 100644 --- a/lib/CodeGen/CodeGenTypes.h +++ b/lib/CodeGen/CodeGenTypes.h @@ -218,10 +218,6 @@ class CodeGenTypes { /// gets assigned to a class. void RefreshTypeCacheForClass(const CXXRecordDecl *RD); - /// getNullaryFunctionInfo - Get the function info for a void() - /// function with standard CC. - const CGFunctionInfo &arrangeNullaryFunction(); - // The arrangement methods are split into three families: // - those meant to drive the signature and prologue/epilogue // of a function declaration or definition, From 5e78c795294e3c8e01148fbfea899df34da18110 Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 4 Apr 2016 18:33:00 +0000 Subject: [PATCH 468/742] Add a couple of convenience operations to CharUnits. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265323 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/CharUnits.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/clang/AST/CharUnits.h b/include/clang/AST/CharUnits.h index 3a819d5ed63..b2da51c23b8 100644 --- a/include/clang/AST/CharUnits.h +++ b/include/clang/AST/CharUnits.h @@ -142,9 +142,17 @@ namespace clang { CharUnits operator* (QuantityType N) const { return CharUnits(Quantity * N); } + CharUnits &operator*= (QuantityType N) { + Quantity *= N; + return *this; + } CharUnits operator/ (QuantityType N) const { return CharUnits(Quantity / N); } + CharUnits operator/= (QuantityType N) { + Quantity /= N; + return *this; + } QuantityType operator/ (const CharUnits &Other) const { return Quantity / Other.Quantity; } From 3dd4a7d439eb362650888116d1dfbbc67f3ee61a Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 4 Apr 2016 18:33:08 +0000 Subject: [PATCH 469/742] IRGen-level lowering for the Swift calling convention. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265324 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/CodeGen/SwiftCallingConv.h | 168 +++++ lib/CodeGen/ABIInfo.h | 51 +- lib/CodeGen/CGCall.cpp | 178 ++++- lib/CodeGen/CMakeLists.txt | 1 + lib/CodeGen/CodeGenFunction.h | 4 +- lib/CodeGen/CodeGenModule.h | 2 +- lib/CodeGen/CodeGenTypes.h | 2 +- lib/CodeGen/SwiftCallingConv.cpp | 830 +++++++++++++++++++++++ lib/CodeGen/TargetInfo.cpp | 87 ++- lib/CodeGen/TargetInfo.h | 5 +- test/CodeGen/arm-swiftcall.c | 496 ++++++++++++++ test/CodeGenCXX/arm-swiftcall.cpp | 115 ++++ 12 files changed, 1894 insertions(+), 45 deletions(-) create mode 100644 include/clang/CodeGen/SwiftCallingConv.h create mode 100644 lib/CodeGen/SwiftCallingConv.cpp create mode 100644 test/CodeGen/arm-swiftcall.c create mode 100644 test/CodeGenCXX/arm-swiftcall.cpp diff --git a/include/clang/CodeGen/SwiftCallingConv.h b/include/clang/CodeGen/SwiftCallingConv.h new file mode 100644 index 00000000000..f9c2fd94ca8 --- /dev/null +++ b/include/clang/CodeGen/SwiftCallingConv.h @@ -0,0 +1,168 @@ +//==-- SwiftCallingConv.h - Swift ABI lowering -----------------------------==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Defines constants and types related to Swift ABI lowering. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CODEGEN_SWIFTCALLINGCONV_H +#define LLVM_CLANG_CODEGEN_SWIFTCALLINGCONV_H + +#include "clang/AST/CanonicalType.h" +#include "clang/AST/CharUnits.h" +#include "clang/AST/Type.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/Support/TrailingObjects.h" +#include + +namespace llvm { + class IntegerType; + class Type; + class StructType; + class VectorType; +} + +namespace clang { +class Decl; +class FieldDecl; +class ASTRecordLayout; + +namespace CodeGen { +class ABIArgInfo; +class CodeGenModule; +class CGFunctionInfo; + +namespace swiftcall { + +class SwiftAggLowering { + CodeGenModule &CGM; + + struct StorageEntry { + CharUnits Begin; + CharUnits End; + llvm::Type *Type; + + CharUnits getWidth() const { + return End - Begin; + } + }; + SmallVector Entries; + bool Finished = false; + +public: + SwiftAggLowering(CodeGenModule &CGM) : CGM(CGM) {} + + void addOpaqueData(CharUnits begin, CharUnits end) { + addEntry(nullptr, begin, end); + } + + void addTypedData(QualType type, CharUnits begin); + void addTypedData(const RecordDecl *record, CharUnits begin); + void addTypedData(const RecordDecl *record, CharUnits begin, + const ASTRecordLayout &layout); + void addTypedData(llvm::Type *type, CharUnits begin); + void addTypedData(llvm::Type *type, CharUnits begin, CharUnits end); + + void finish(); + + /// Does this lowering require passing any data? + bool empty() const { + assert(Finished && "didn't finish lowering before calling empty()"); + return Entries.empty(); + } + + /// According to the target Swift ABI, should a value with this lowering + /// be passed indirectly? + /// + /// Note that this decision is based purely on the data layout of the + /// value and does not consider whether the type is address-only, + /// must be passed indirectly to match a function abstraction pattern, or + /// anything else that is expected to be handled by high-level lowering. + /// + /// \param asReturnValue - if true, answer whether it should be passed + /// indirectly as a return value; if false, answer whether it should be + /// passed indirectly as an argument + bool shouldPassIndirectly(bool asReturnValue) const; + + using EnumerationCallback = + llvm::function_ref; + + /// Enumerate the expanded components of this type. + /// + /// The component types will always be legal vector, floating-point, + /// integer, or pointer types. + void enumerateComponents(EnumerationCallback callback) const; + + /// Return the types for a coerce-and-expand operation. + /// + /// The first type matches the memory layout of the data that's been + /// added to this structure, including explicit [N x i8] arrays for any + /// internal padding. + /// + /// The second type removes any internal padding members and, if only + /// one element remains, is simply that element type. + std::pair getCoerceAndExpandTypes() const; + +private: + void addBitFieldData(const FieldDecl *field, CharUnits begin, + uint64_t bitOffset); + void addLegalTypedData(llvm::Type *type, CharUnits begin, CharUnits end); + void addEntry(llvm::Type *type, CharUnits begin, CharUnits end); + void splitVectorEntry(unsigned index); +}; + +/// Return the maximum voluntary integer size for the current target. +CharUnits getMaximumVoluntaryIntegerSize(CodeGenModule &CGM); + +/// Return the Swift CC's notion of the natural alignment of a type. +CharUnits getNaturalAlignment(CodeGenModule &CGM, llvm::Type *type); + +/// Is the given integer type "legal" for Swift's perspective on the +/// current platform? +bool isLegalIntegerType(CodeGenModule &CGM, llvm::IntegerType *type); + +/// Is the given vector type "legal" for Swift's perspective on the +/// current platform? +bool isLegalVectorType(CodeGenModule &CGM, CharUnits vectorSize, + llvm::VectorType *vectorTy); +bool isLegalVectorType(CodeGenModule &CGM, CharUnits vectorSize, + llvm::Type *eltTy, unsigned numElts); + +/// Minimally split a legal vector type. +std::pair +splitLegalVectorType(CodeGenModule &CGM, CharUnits vectorSize, + llvm::VectorType *vectorTy); + +/// Turn a vector type in a sequence of legal component vector types. +/// +/// The caller may assume that the sum of the data sizes of the resulting +/// types will equal the data size of the vector type. +void legalizeVectorType(CodeGenModule &CGM, CharUnits vectorSize, + llvm::VectorType *vectorTy, + llvm::SmallVectorImpl &types); + +/// Should a C++ record type be passed and returned indirectly? +bool shouldPassCXXRecordIndirectly(CodeGenModule &CGM, + const CXXRecordDecl *record); + +/// Classify the rules for how to return a particular type. +ABIArgInfo classifyReturnType(CodeGenModule &CGM, CanQualType type); + +/// Classify the rules for how to pass a particular type. +ABIArgInfo classifyArgumentType(CodeGenModule &CGM, CanQualType type); + +/// Compute the ABI information of a swiftcall function. This is a +/// private interface for Clang. +void computeABIInfo(CodeGenModule &CGM, CGFunctionInfo &FI); + +} // end namespace swiftcall +} // end namespace CodeGen +} // end namespace clang + +#endif diff --git a/lib/CodeGen/ABIInfo.h b/lib/CodeGen/ABIInfo.h index a65f2708561..bf462906506 100644 --- a/lib/CodeGen/ABIInfo.h +++ b/lib/CodeGen/ABIInfo.h @@ -18,20 +18,25 @@ namespace llvm { class Value; class LLVMContext; class DataLayout; + class Type; } namespace clang { class ASTContext; class TargetInfo; - namespace CodeGen { - class ABIArgInfo; - class Address; - class CGCXXABI; - class CGFunctionInfo; - class CodeGenFunction; - class CodeGenTypes; - } +namespace CodeGen { + class ABIArgInfo; + class Address; + class CGCXXABI; + class CGFunctionInfo; + class CodeGenFunction; + class CodeGenTypes; + class SwiftABIInfo; + +namespace swiftcall { + class SwiftAggLowering; +} // FIXME: All of this stuff should be part of the target interface // somehow. It is currently here because it is not clear how to factor @@ -55,6 +60,8 @@ namespace clang { virtual ~ABIInfo(); + virtual bool supportsSwift() const { return false; } + CodeGen::CGCXXABI &getCXXABI() const; ASTContext &getContext() const; llvm::LLVMContext &getVMContext() const; @@ -110,7 +117,35 @@ namespace clang { CodeGen::ABIArgInfo getNaturalAlignIndirectInReg(QualType Ty, bool Realign = false) const; + + }; + + /// A refining implementation of ABIInfo for targets that support swiftcall. + /// + /// If we find ourselves wanting multiple such refinements, they'll probably + /// be independent refinements, and we should probably find another way + /// to do it than simple inheritance. + class SwiftABIInfo : public ABIInfo { + public: + SwiftABIInfo(CodeGen::CodeGenTypes &cgt) : ABIInfo(cgt) {} + + bool supportsSwift() const final override { return true; } + + virtual bool shouldPassIndirectlyForSwift(CharUnits totalSize, + ArrayRef types, + bool asReturnValue) const = 0; + + virtual bool isLegalVectorTypeForSwift(CharUnits totalSize, + llvm::Type *eltTy, + unsigned elts) const; + + static bool classof(const ABIInfo *info) { + return info->supportsSwift(); + } + }; + +} // end namespace CodeGen } // end namespace clang #endif diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index ebdcd705bee..ef32a9363ba 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -26,6 +26,7 @@ #include "clang/Basic/TargetBuiltins.h" #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/CGFunctionInfo.h" +#include "clang/CodeGen/SwiftCallingConv.h" #include "clang/Frontend/CodeGenOptions.h" #include "llvm/ADT/StringExtras.h" #include "llvm/IR/Attributes.h" @@ -59,6 +60,7 @@ static unsigned ClangCallConvToLLVMCallConv(CallingConv CC) { case CC_SpirKernel: return llvm::CallingConv::SPIR_KERNEL; case CC_PreserveMost: return llvm::CallingConv::PreserveMost; case CC_PreserveAll: return llvm::CallingConv::PreserveAll; + case CC_Swift: return llvm::CallingConv::Swift; } } @@ -109,7 +111,7 @@ static void appendParameterTypes(const CodeGenTypes &CGT, auto protoParamInfos = FPT->getExtParameterInfos(); paramInfos.reserve(prefix.size() + protoParamInfos.size()); paramInfos.resize(prefix.size()); - paramInfos.append(paramInfos.begin(), paramInfos.end()); + paramInfos.append(protoParamInfos.begin(), protoParamInfos.end()); } // Fast path: unknown target. @@ -590,7 +592,6 @@ CodeGenTypes::arrangeBuiltinFunctionDeclaration(CanQualType resultType, argTypes, FunctionType::ExtInfo(), {}, RequiredArgs::All); } - /// Arrange a call to a C++ method, passing the given arguments. const CGFunctionInfo & CodeGenTypes::arrangeCXXMethodCall(const CallArgList &args, @@ -679,7 +680,11 @@ CodeGenTypes::arrangeLLVMFunctionInfo(CanQualType resultType, assert(inserted && "Recursively being processed?"); // Compute ABI information. - getABIInfo().computeInfo(*FI); + if (info.getCC() != CC_Swift) { + getABIInfo().computeInfo(*FI); + } else { + swiftcall::computeABIInfo(CGM, *FI); + } // Loop over all of the computed argument and return value info. If any of // them are direct or extend without a specified coerce type, specify the @@ -917,7 +922,7 @@ static void forConstantArrayExpansion(CodeGenFunction &CGF, } void CodeGenFunction::ExpandTypeFromArgs( - QualType Ty, LValue LV, SmallVectorImpl::iterator &AI) { + QualType Ty, LValue LV, SmallVectorImpl::iterator &AI) { assert(LV.isSimple() && "Unexpected non-simple lvalue during struct expansion."); @@ -1801,10 +1806,13 @@ void CodeGenModule::ConstructAttributeList( getLLVMContext(), llvm::AttributeSet::ReturnIndex, RetAttrs)); } + bool hasUsedSRet = false; + // Attach attributes to sret. if (IRFunctionArgs.hasSRetArg()) { llvm::AttrBuilder SRETAttrs; SRETAttrs.addAttribute(llvm::Attribute::StructRet); + hasUsedSRet = true; if (RetAI.getInReg()) SRETAttrs.addAttribute(llvm::Attribute::InReg); PAL.push_back(llvm::AttributeSet::get( @@ -1908,6 +1916,41 @@ void CodeGenModule::ConstructAttributeList( Attrs.addAttribute(llvm::Attribute::NonNull); } + switch (FI.getExtParameterInfo(ArgNo).getABI()) { + case ParameterABI::Ordinary: + break; + + case ParameterABI::SwiftIndirectResult: { + // Add 'sret' if we haven't already used it for something, but + // only if the result is void. + if (!hasUsedSRet && RetTy->isVoidType()) { + Attrs.addAttribute(llvm::Attribute::StructRet); + hasUsedSRet = true; + } + + // Add 'noalias' in either case. + Attrs.addAttribute(llvm::Attribute::NoAlias); + + // Add 'dereferenceable' and 'alignment'. + auto PTy = ParamType->getPointeeType(); + if (!PTy->isIncompleteType() && PTy->isConstantSizeType()) { + auto info = getContext().getTypeInfoInChars(PTy); + Attrs.addDereferenceableAttr(info.first.getQuantity()); + Attrs.addAttribute(llvm::Attribute::getWithAlignment(getLLVMContext(), + info.second.getQuantity())); + } + break; + } + + case ParameterABI::SwiftErrorResult: + Attrs.addAttribute(llvm::Attribute::SwiftError); + break; + + case ParameterABI::SwiftContext: + Attrs.addAttribute(llvm::Attribute::SwiftSelf); + break; + } + if (Attrs.hasAttributes()) { unsigned FirstIRArg, NumIRArgs; std::tie(FirstIRArg, NumIRArgs) = IRFunctionArgs.getIRArgs(ArgNo); @@ -1973,6 +2016,18 @@ static const NonNullAttr *getNonNullAttr(const Decl *FD, const ParmVarDecl *PVD, return nullptr; } +namespace { + struct CopyBackSwiftError final : EHScopeStack::Cleanup { + Address Temp; + Address Arg; + CopyBackSwiftError(Address temp, Address arg) : Temp(temp), Arg(arg) {} + void Emit(CodeGenFunction &CGF, Flags flags) override { + llvm::Value *errorValue = CGF.Builder.CreateLoad(Temp); + CGF.Builder.CreateStore(errorValue, Arg); + } + }; +} + void CodeGenFunction::EmitFunctionProlog(const CGFunctionInfo &FI, llvm::Function *Fn, const FunctionArgList &Args) { @@ -1998,7 +2053,7 @@ void CodeGenFunction::EmitFunctionProlog(const CGFunctionInfo &FI, ClangToLLVMArgMapping IRFunctionArgs(CGM.getContext(), FI); // Flattened function arguments. - SmallVector FnArgs; + SmallVector FnArgs; FnArgs.reserve(IRFunctionArgs.totalIRArgs()); for (auto &Arg : Fn->args()) { FnArgs.push_back(&Arg); @@ -2019,7 +2074,7 @@ void CodeGenFunction::EmitFunctionProlog(const CGFunctionInfo &FI, // Name the struct return parameter. if (IRFunctionArgs.hasSRetArg()) { - auto AI = FnArgs[IRFunctionArgs.getSRetArgNo()]; + auto AI = cast(FnArgs[IRFunctionArgs.getSRetArgNo()]); AI->setName("agg.result"); AI->addAttr(llvm::AttributeSet::get(getLLVMContext(), AI->getArgNo() + 1, llvm::Attribute::NoAlias)); @@ -2107,8 +2162,8 @@ void CodeGenFunction::EmitFunctionProlog(const CGFunctionInfo &FI, ArgI.getCoerceToType() == ConvertType(Ty) && ArgI.getDirectOffset() == 0) { assert(NumIRArgs == 1); - auto AI = FnArgs[FirstIRArg]; - llvm::Value *V = AI; + llvm::Value *V = FnArgs[FirstIRArg]; + auto AI = cast(V); if (const ParmVarDecl *PVD = dyn_cast(Arg)) { if (getNonNullAttr(CurCodeDecl, PVD, PVD->getType(), @@ -2177,6 +2232,25 @@ void CodeGenFunction::EmitFunctionProlog(const CGFunctionInfo &FI, AI->getArgNo() + 1, llvm::Attribute::NoAlias)); + // LLVM expects swifterror parameters to be used in very restricted + // ways. Copy the value into a less-restricted temporary. + if (FI.getExtParameterInfo(ArgNo).getABI() + == ParameterABI::SwiftErrorResult) { + QualType pointeeTy = Ty->getPointeeType(); + assert(pointeeTy->isPointerType()); + Address temp = + CreateMemTemp(pointeeTy, getPointerAlign(), "swifterror.temp"); + Address arg = Address(V, getContext().getTypeAlignInChars(pointeeTy)); + llvm::Value *incomingErrorValue = Builder.CreateLoad(arg); + Builder.CreateStore(incomingErrorValue, temp); + V = temp.getPointer(); + + // Push a cleanup to copy the value back at the end of the function. + // The convention does not guarantee that the value will be written + // back if the function exits with an unwind exception. + EHStack.pushCleanup(NormalCleanup, temp, arg); + } + // Ensure the argument is the correct type. if (V->getType() != ArgI.getCoerceToType()) V = Builder.CreateBitCast(V, ArgI.getCoerceToType()); @@ -3463,6 +3537,9 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, } } + Address swiftErrorTemp = Address::invalid(); + Address swiftErrorArg = Address::invalid(); + assert(CallInfo.arg_size() == CallArgs.size() && "Mismatch between function signature & arguments."); unsigned ArgNo = 0; @@ -3569,6 +3646,25 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, else V = Builder.CreateLoad(RV.getAggregateAddress()); + // Implement swifterror by copying into a new swifterror argument. + // We'll write back in the normal path out of the call. + if (CallInfo.getExtParameterInfo(ArgNo).getABI() + == ParameterABI::SwiftErrorResult) { + assert(!swiftErrorTemp.isValid() && "multiple swifterror args"); + + QualType pointeeTy = I->Ty->getPointeeType(); + swiftErrorArg = + Address(V, getContext().getTypeAlignInChars(pointeeTy)); + + swiftErrorTemp = + CreateMemTemp(pointeeTy, getPointerAlign(), "swifterror.temp"); + V = swiftErrorTemp.getPointer(); + cast(V)->setSwiftError(true); + + llvm::Value *errorValue = Builder.CreateLoad(swiftErrorArg); + Builder.CreateStore(errorValue, swiftErrorTemp); + } + // We might have to widen integers, but we should never truncate. if (ArgInfo.getCoerceToType() != V->getType() && V->getType()->isIntegerTy()) @@ -3579,6 +3675,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, if (FirstIRArg < IRFuncTy->getNumParams() && V->getType() != IRFuncTy->getParamType(FirstIRArg)) V = Builder.CreateBitCast(V, IRFuncTy->getParamType(FirstIRArg)); + IRCallArgs[FirstIRArg] = V; break; } @@ -3638,13 +3735,31 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, } case ABIArgInfo::CoerceAndExpand: { - assert(RV.isAggregate() && - "CoerceAndExpand does not support non-aggregate types yet"); - auto coercionType = ArgInfo.getCoerceAndExpandType(); auto layout = CGM.getDataLayout().getStructLayout(coercionType); - Address addr = RV.getAggregateAddress(); + llvm::Value *tempSize = nullptr; + Address addr = Address::invalid(); + if (RV.isAggregate()) { + addr = RV.getAggregateAddress(); + } else { + assert(RV.isScalar()); // complex should always just be direct + + llvm::Type *scalarType = RV.getScalarVal()->getType(); + auto scalarSize = CGM.getDataLayout().getTypeAllocSize(scalarType); + auto scalarAlign = CGM.getDataLayout().getPrefTypeAlignment(scalarType); + + tempSize = llvm::ConstantInt::get(CGM.Int64Ty, scalarSize); + + // Materialize to a temporary. + addr = CreateTempAlloca(RV.getScalarVal()->getType(), + CharUnits::fromQuantity(std::max(layout->getAlignment(), + scalarAlign))); + EmitLifetimeStart(scalarSize, addr.getPointer()); + + Builder.CreateStore(RV.getScalarVal(), addr); + } + addr = Builder.CreateElementBitCast(addr, coercionType); unsigned IRArgPos = FirstIRArg; @@ -3657,6 +3772,10 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, } assert(IRArgPos == FirstIRArg + NumIRArgs); + if (tempSize) { + EmitLifetimeEnd(tempSize, addr.getPointer()); + } + break; } @@ -3828,6 +3947,12 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, if (!CI->getType()->isVoidTy()) CI->setName("call"); + // Perform the swifterror writeback. + if (swiftErrorTemp.isValid()) { + llvm::Value *errorResult = Builder.CreateLoad(swiftErrorTemp); + Builder.CreateStore(errorResult, swiftErrorArg); + } + // Emit any writebacks immediately. Arguably this should happen // after any return-value munging. if (CallArgs.hasWritebacks()) @@ -3845,15 +3970,6 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, RValue Ret = [&] { switch (RetAI.getKind()) { - case ABIArgInfo::InAlloca: - case ABIArgInfo::Indirect: { - RValue ret = convertTempToRValue(SRetPtr, RetTy, SourceLocation()); - if (UnusedReturnSize) - EmitLifetimeEnd(llvm::ConstantInt::get(Int64Ty, UnusedReturnSize), - SRetPtr.getPointer()); - return ret; - } - case ABIArgInfo::CoerceAndExpand: { auto coercionType = RetAI.getCoerceAndExpandType(); auto layout = CGM.getDataLayout().getStructLayout(coercionType); @@ -3861,15 +3977,31 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, Address addr = SRetPtr; addr = Builder.CreateElementBitCast(addr, coercionType); + assert(CI->getType() == RetAI.getUnpaddedCoerceAndExpandType()); + bool requiresExtract = isa(CI->getType()); + unsigned unpaddedIndex = 0; for (unsigned i = 0, e = coercionType->getNumElements(); i != e; ++i) { llvm::Type *eltType = coercionType->getElementType(i); if (ABIArgInfo::isPaddingForCoerceAndExpand(eltType)) continue; Address eltAddr = Builder.CreateStructGEP(addr, i, layout); - llvm::Value *elt = Builder.CreateExtractValue(CI, unpaddedIndex++); + llvm::Value *elt = CI; + if (requiresExtract) + elt = Builder.CreateExtractValue(elt, unpaddedIndex++); + else + assert(unpaddedIndex == 0); Builder.CreateStore(elt, eltAddr); } - break; + // FALLTHROUGH + } + + case ABIArgInfo::InAlloca: + case ABIArgInfo::Indirect: { + RValue ret = convertTempToRValue(SRetPtr, RetTy, SourceLocation()); + if (UnusedReturnSize) + EmitLifetimeEnd(llvm::ConstantInt::get(Int64Ty, UnusedReturnSize), + SRetPtr.getPointer()); + return ret; } case ABIArgInfo::Ignore: diff --git a/lib/CodeGen/CMakeLists.txt b/lib/CodeGen/CMakeLists.txt index 10bda76b6b6..6ec1ebbf53e 100644 --- a/lib/CodeGen/CMakeLists.txt +++ b/lib/CodeGen/CMakeLists.txt @@ -74,6 +74,7 @@ add_clang_library(clangCodeGen ModuleBuilder.cpp ObjectFilePCHContainerOperations.cpp SanitizerMetadata.cpp + SwiftCallingConv.cpp TargetInfo.cpp DEPENDS diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index 961d6aed0f6..ed2718e16cd 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -68,7 +68,6 @@ class ObjCMethodDecl; class ObjCImplementationDecl; class ObjCPropertyImplDecl; class TargetInfo; -class TargetCodeGenInfo; class VarDecl; class ObjCForCollectionStmt; class ObjCAtTryStmt; @@ -86,6 +85,7 @@ class BlockByrefHelpers; class BlockByrefInfo; class BlockFlags; class BlockFieldFlags; +class TargetCodeGenInfo; /// The kind of evaluation to perform on values of a particular /// type. Basically, is the code in CGExprScalar, CGExprComplex, or @@ -3067,7 +3067,7 @@ class CodeGenFunction : public CodeGenTypeCache { /// /// \param AI - The first function argument of the expansion. void ExpandTypeFromArgs(QualType Ty, LValue Dst, - SmallVectorImpl::iterator &AI); + SmallVectorImpl::iterator &AI); /// ExpandTypeToArgs - Expand an RValue \arg RV, with the LLVM type for \arg /// Ty, into individual arguments on the provided vector \arg IRCallArgs, diff --git a/lib/CodeGen/CodeGenModule.h b/lib/CodeGen/CodeGenModule.h index efebcdac702..b012127432a 100644 --- a/lib/CodeGen/CodeGenModule.h +++ b/lib/CodeGen/CodeGenModule.h @@ -48,7 +48,6 @@ class IndexedInstrProfReader; } namespace clang { -class TargetCodeGenInfo; class ASTContext; class AtomicType; class FunctionDecl; @@ -92,6 +91,7 @@ class CGCUDARuntime; class BlockFieldFlags; class FunctionArgList; class CoverageMappingModuleGen; +class TargetCodeGenInfo; struct OrderGlobalInits { unsigned int priority; diff --git a/lib/CodeGen/CodeGenTypes.h b/lib/CodeGen/CodeGenTypes.h index 34f6b611365..5a2f5c391f4 100644 --- a/lib/CodeGen/CodeGenTypes.h +++ b/lib/CodeGen/CodeGenTypes.h @@ -31,7 +31,6 @@ class StructType; } namespace clang { -class ABIInfo; class ASTContext; template class CanQual; class CXXConstructorDecl; @@ -51,6 +50,7 @@ class Type; typedef CanQual CanQualType; namespace CodeGen { +class ABIInfo; class CGCXXABI; class CGRecordLayout; class CodeGenModule; diff --git a/lib/CodeGen/SwiftCallingConv.cpp b/lib/CodeGen/SwiftCallingConv.cpp new file mode 100644 index 00000000000..6fae19f2779 --- /dev/null +++ b/lib/CodeGen/SwiftCallingConv.cpp @@ -0,0 +1,830 @@ +//===--- SwiftCallingConv.cpp - Lowering for the Swift calling convention -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implementation of the abstract lowering for the Swift calling convention. +// +//===----------------------------------------------------------------------===// + +#include "clang/CodeGen/SwiftCallingConv.h" +#include "clang/Basic/TargetInfo.h" +#include "CodeGenModule.h" +#include "TargetInfo.h" + +using namespace clang; +using namespace CodeGen; +using namespace swiftcall; + +static const SwiftABIInfo &getSwiftABIInfo(CodeGenModule &CGM) { + return cast(CGM.getTargetCodeGenInfo().getABIInfo()); +} + +static bool isPowerOf2(unsigned n) { + return n == (n & -n); +} + +/// Given two types with the same size, try to find a common type. +static llvm::Type *getCommonType(llvm::Type *first, llvm::Type *second) { + assert(first != second); + + // Allow pointers to merge with integers, but prefer the integer type. + if (first->isIntegerTy()) { + if (second->isPointerTy()) return first; + } else if (first->isPointerTy()) { + if (second->isIntegerTy()) return second; + if (second->isPointerTy()) return first; + + // Allow two vectors to be merged (given that they have the same size). + // This assumes that we never have two different vector register sets. + } else if (auto firstVecTy = dyn_cast(first)) { + if (auto secondVecTy = dyn_cast(second)) { + if (auto commonTy = getCommonType(firstVecTy->getElementType(), + secondVecTy->getElementType())) { + return (commonTy == firstVecTy->getElementType() ? first : second); + } + } + } + + return nullptr; +} + +static CharUnits getTypeStoreSize(CodeGenModule &CGM, llvm::Type *type) { + return CharUnits::fromQuantity(CGM.getDataLayout().getTypeStoreSize(type)); +} + +void SwiftAggLowering::addTypedData(QualType type, CharUnits begin) { + // Deal with various aggregate types as special cases: + + // Record types. + if (auto recType = type->getAs()) { + addTypedData(recType->getDecl(), begin); + + // Array types. + } else if (type->isArrayType()) { + // Incomplete array types (flexible array members?) don't provide + // data to lay out, and the other cases shouldn't be possible. + auto arrayType = CGM.getContext().getAsConstantArrayType(type); + if (!arrayType) return; + + QualType eltType = arrayType->getElementType(); + auto eltSize = CGM.getContext().getTypeSizeInChars(eltType); + for (uint64_t i = 0, e = arrayType->getSize().getZExtValue(); i != e; ++i) { + addTypedData(eltType, begin + i * eltSize); + } + + // Complex types. + } else if (auto complexType = type->getAs()) { + auto eltType = complexType->getElementType(); + auto eltSize = CGM.getContext().getTypeSizeInChars(eltType); + auto eltLLVMType = CGM.getTypes().ConvertType(eltType); + addTypedData(eltLLVMType, begin, begin + eltSize); + addTypedData(eltLLVMType, begin + eltSize, begin + 2 * eltSize); + + // Member pointer types. + } else if (type->getAs()) { + // Just add it all as opaque. + addOpaqueData(begin, begin + CGM.getContext().getTypeSizeInChars(type)); + + // Everything else is scalar and should not convert as an LLVM aggregate. + } else { + // We intentionally convert as !ForMem because we want to preserve + // that a type was an i1. + auto llvmType = CGM.getTypes().ConvertType(type); + addTypedData(llvmType, begin); + } +} + +void SwiftAggLowering::addTypedData(const RecordDecl *record, CharUnits begin) { + addTypedData(record, begin, CGM.getContext().getASTRecordLayout(record)); +} + +void SwiftAggLowering::addTypedData(const RecordDecl *record, CharUnits begin, + const ASTRecordLayout &layout) { + // Unions are a special case. + if (record->isUnion()) { + for (auto field : record->fields()) { + if (field->isBitField()) { + addBitFieldData(field, begin, 0); + } else { + addTypedData(field->getType(), begin); + } + } + return; + } + + // Note that correctness does not rely on us adding things in + // their actual order of layout; it's just somewhat more efficient + // for the builder. + + // With that in mind, add "early" C++ data. + auto cxxRecord = dyn_cast(record); + if (cxxRecord) { + // - a v-table pointer, if the class adds its own + if (layout.hasOwnVFPtr()) { + addTypedData(CGM.Int8PtrTy, begin); + } + + // - non-virtual bases + for (auto &baseSpecifier : cxxRecord->bases()) { + if (baseSpecifier.isVirtual()) continue; + + auto baseRecord = baseSpecifier.getType()->getAsCXXRecordDecl(); + addTypedData(baseRecord, begin + layout.getBaseClassOffset(baseRecord)); + } + + // - a vbptr if the class adds its own + if (layout.hasOwnVBPtr()) { + addTypedData(CGM.Int8PtrTy, begin + layout.getVBPtrOffset()); + } + } + + // Add fields. + for (auto field : record->fields()) { + auto fieldOffsetInBits = layout.getFieldOffset(field->getFieldIndex()); + if (field->isBitField()) { + addBitFieldData(field, begin, fieldOffsetInBits); + } else { + addTypedData(field->getType(), + begin + CGM.getContext().toCharUnitsFromBits(fieldOffsetInBits)); + } + } + + // Add "late" C++ data: + if (cxxRecord) { + // - virtual bases + for (auto &vbaseSpecifier : cxxRecord->vbases()) { + auto baseRecord = vbaseSpecifier.getType()->getAsCXXRecordDecl(); + addTypedData(baseRecord, begin + layout.getVBaseClassOffset(baseRecord)); + } + } +} + +void SwiftAggLowering::addBitFieldData(const FieldDecl *bitfield, + CharUnits recordBegin, + uint64_t bitfieldBitBegin) { + assert(bitfield->isBitField()); + auto &ctx = CGM.getContext(); + auto width = bitfield->getBitWidthValue(ctx); + + // We can ignore zero-width bit-fields. + if (width == 0) return; + + // toCharUnitsFromBits rounds down. + CharUnits bitfieldByteBegin = ctx.toCharUnitsFromBits(bitfieldBitBegin); + + // Find the offset of the last byte that is partially occupied by the + // bit-field; since we otherwise expect exclusive ends, the end is the + // next byte. + uint64_t bitfieldBitLast = bitfieldBitBegin + width - 1; + CharUnits bitfieldByteEnd = + ctx.toCharUnitsFromBits(bitfieldBitLast) + CharUnits::One(); + addOpaqueData(recordBegin + bitfieldByteBegin, + recordBegin + bitfieldByteEnd); +} + +void SwiftAggLowering::addTypedData(llvm::Type *type, CharUnits begin) { + assert(type && "didn't provide type for typed data"); + addTypedData(type, begin, begin + getTypeStoreSize(CGM, type)); +} + +void SwiftAggLowering::addTypedData(llvm::Type *type, + CharUnits begin, CharUnits end) { + assert(type && "didn't provide type for typed data"); + assert(getTypeStoreSize(CGM, type) == end - begin); + + // Legalize vector types. + if (auto vecTy = dyn_cast(type)) { + SmallVector componentTys; + legalizeVectorType(CGM, end - begin, vecTy, componentTys); + assert(componentTys.size() >= 1); + + // Walk the initial components. + for (size_t i = 0, e = componentTys.size(); i != e - 1; ++i) { + llvm::Type *componentTy = componentTys[i]; + auto componentSize = getTypeStoreSize(CGM, componentTy); + assert(componentSize < end - begin); + addLegalTypedData(componentTy, begin, begin + componentSize); + begin += componentSize; + } + + return addLegalTypedData(componentTys.back(), begin, end); + } + + // Legalize integer types. + if (auto intTy = dyn_cast(type)) { + if (!isLegalIntegerType(CGM, intTy)) + return addOpaqueData(begin, end); + } + + // All other types should be legal. + return addLegalTypedData(type, begin, end); +} + +void SwiftAggLowering::addLegalTypedData(llvm::Type *type, + CharUnits begin, CharUnits end) { + // Require the type to be naturally aligned. + if (!begin.isZero() && !begin.isMultipleOf(getNaturalAlignment(CGM, type))) { + + // Try splitting vector types. + if (auto vecTy = dyn_cast(type)) { + auto split = splitLegalVectorType(CGM, end - begin, vecTy); + auto eltTy = split.first; + auto numElts = split.second; + + auto eltSize = (end - begin) / numElts; + assert(eltSize == getTypeStoreSize(CGM, eltTy)); + for (size_t i = 0, e = numElts; i != e; ++i) { + addLegalTypedData(type, begin, begin + eltSize); + begin += eltSize; + } + assert(begin == end); + return; + } + + return addOpaqueData(begin, end); + } + + addEntry(type, begin, end); +} + +void SwiftAggLowering::addEntry(llvm::Type *type, + CharUnits begin, CharUnits end) { + assert(!type || + (!isa(type) && !isa(type)) && + "cannot add aggregate-typed data"); + assert(!type || begin.isMultipleOf(getNaturalAlignment(CGM, type))); + + // Fast path: we can just add entries to the end. + if (Entries.empty() || Entries.back().End <= begin) { + Entries.push_back({begin, end, type}); + return; + } + + // Find the first existing entry that ends after the start of the new data. + // TODO: do a binary search if Entries is big enough for it to matter. + size_t index = Entries.size() - 1; + while (index != 0) { + if (Entries[index - 1].End <= begin) break; + --index; + } + + // The entry ends after the start of the new data. + // If the entry starts after the end of the new data, there's no conflict. + if (Entries[index].Begin >= end) { + // This insertion is potentially O(n), but the way we generally build + // these layouts makes that unlikely to matter: we'd need a union of + // several very large types. + Entries.insert(Entries.begin() + index, {begin, end, type}); + return; + } + + // Otherwise, the ranges overlap. The new range might also overlap + // with later ranges. +restartAfterSplit: + + // Simplest case: an exact overlap. + if (Entries[index].Begin == begin && Entries[index].End == end) { + // If the types match exactly, great. + if (Entries[index].Type == type) return; + + // If either type is opaque, make the entry opaque and return. + if (Entries[index].Type == nullptr) { + return; + } else if (type == nullptr) { + Entries[index].Type = nullptr; + return; + } + + // If they disagree in an ABI-agnostic way, just resolve the conflict + // arbitrarily. + if (auto entryType = getCommonType(Entries[index].Type, type)) { + Entries[index].Type = entryType; + return; + } + + // Otherwise, make the entry opaque. + Entries[index].Type = nullptr; + return; + } + + // Okay, we have an overlapping conflict of some sort. + + // If we have a vector type, split it. + if (auto vecTy = dyn_cast_or_null(type)) { + auto eltTy = vecTy->getElementType(); + CharUnits eltSize = (end - begin) / vecTy->getNumElements(); + assert(eltSize == getTypeStoreSize(CGM, eltTy)); + for (unsigned i = 0, e = vecTy->getNumElements(); i != e; ++i) { + addEntry(eltTy, begin, begin + eltSize); + begin += eltSize; + } + assert(begin == end); + return; + } + + // If the entry is a vector type, split it and try again. + if (Entries[index].Type && Entries[index].Type->isVectorTy()) { + splitVectorEntry(index); + goto restartAfterSplit; + } + + // Okay, we have no choice but to make the existing entry opaque. + + Entries[index].Type = nullptr; + + // Stretch the start of the entry to the beginning of the range. + if (begin < Entries[index].Begin) { + Entries[index].Begin = begin; + assert(index == 0 || begin >= Entries[index - 1].End); + } + + // Stretch the end of the entry to the end of the range; but if we run + // into the start of the next entry, just leave the range there and repeat. + while (end > Entries[index].End) { + assert(Entries[index].Type == nullptr); + + // If the range doesn't overlap the next entry, we're done. + if (index == Entries.size() - 1 || end <= Entries[index + 1].Begin) { + Entries[index].End = end; + break; + } + + // Otherwise, stretch to the start of the next entry. + Entries[index].End = Entries[index + 1].Begin; + + // Continue with the next entry. + index++; + + // This entry needs to be made opaque if it is not already. + if (Entries[index].Type == nullptr) + continue; + + // Split vector entries unless we completely subsume them. + if (Entries[index].Type->isVectorTy() && + end < Entries[index].End) { + splitVectorEntry(index); + } + + // Make the entry opaque. + Entries[index].Type = nullptr; + } +} + +/// Replace the entry of vector type at offset 'index' with a sequence +/// of its component vectors. +void SwiftAggLowering::splitVectorEntry(unsigned index) { + auto vecTy = cast(Entries[index].Type); + auto split = splitLegalVectorType(CGM, Entries[index].getWidth(), vecTy); + + auto eltTy = split.first; + CharUnits eltSize = getTypeStoreSize(CGM, eltTy); + auto numElts = split.second; + Entries.insert(&Entries[index + 1], numElts - 1, StorageEntry()); + + CharUnits begin = Entries[index].Begin; + for (unsigned i = 0; i != numElts; ++i) { + Entries[index].Type = eltTy; + Entries[index].Begin = begin; + Entries[index].End = begin + eltSize; + begin += eltSize; + } +} + +/// Given a power-of-two unit size, return the offset of the aligned unit +/// of that size which contains the given offset. +/// +/// In other words, round down to the nearest multiple of the unit size. +static CharUnits getOffsetAtStartOfUnit(CharUnits offset, CharUnits unitSize) { + assert(isPowerOf2(unitSize.getQuantity())); + auto unitMask = ~(unitSize.getQuantity() - 1); + return CharUnits::fromQuantity(offset.getQuantity() & unitMask); +} + +static bool areBytesInSameUnit(CharUnits first, CharUnits second, + CharUnits chunkSize) { + return getOffsetAtStartOfUnit(first, chunkSize) + == getOffsetAtStartOfUnit(second, chunkSize); +} + +void SwiftAggLowering::finish() { + if (Entries.empty()) { + Finished = true; + return; + } + + // We logically split the layout down into a series of chunks of this size, + // which is generally the size of a pointer. + const CharUnits chunkSize = getMaximumVoluntaryIntegerSize(CGM); + + // First pass: if two entries share a chunk, make them both opaque + // and stretch one to meet the next. + bool hasOpaqueEntries = (Entries[0].Type == nullptr); + for (size_t i = 1, e = Entries.size(); i != e; ++i) { + if (areBytesInSameUnit(Entries[i - 1].End - CharUnits::One(), + Entries[i].Begin, chunkSize)) { + Entries[i - 1].Type = nullptr; + Entries[i].Type = nullptr; + Entries[i - 1].End = Entries[i].Begin; + hasOpaqueEntries = true; + + } else if (Entries[i].Type == nullptr) { + hasOpaqueEntries = true; + } + } + + // The rest of the algorithm leaves non-opaque entries alone, so if we + // have no opaque entries, we're done. + if (!hasOpaqueEntries) { + Finished = true; + return; + } + + // Okay, move the entries to a temporary and rebuild Entries. + auto orig = std::move(Entries); + assert(Entries.empty()); + + for (size_t i = 0, e = orig.size(); i != e; ++i) { + // Just copy over non-opaque entries. + if (orig[i].Type != nullptr) { + Entries.push_back(orig[i]); + continue; + } + + // Scan forward to determine the full extent of the next opaque range. + // We know from the first pass that only contiguous ranges will overlap + // the same aligned chunk. + auto begin = orig[i].Begin; + auto end = orig[i].End; + while (i + 1 != e && + orig[i + 1].Type == nullptr && + end == orig[i + 1].Begin) { + end = orig[i + 1].End; + i++; + } + + // Add an entry per intersected chunk. + do { + // Find the smallest aligned storage unit in the maximal aligned + // storage unit containing 'begin' that contains all the bytes in + // the intersection between the range and this chunk. + CharUnits localBegin = begin; + CharUnits chunkBegin = getOffsetAtStartOfUnit(localBegin, chunkSize); + CharUnits chunkEnd = chunkBegin + chunkSize; + CharUnits localEnd = std::min(end, chunkEnd); + + // Just do a simple loop over ever-increasing unit sizes. + CharUnits unitSize = CharUnits::One(); + CharUnits unitBegin, unitEnd; + for (; ; unitSize *= 2) { + assert(unitSize <= chunkSize); + unitBegin = getOffsetAtStartOfUnit(localBegin, unitSize); + unitEnd = unitBegin + unitSize; + if (unitEnd >= localEnd) break; + } + + // Add an entry for this unit. + auto entryTy = + llvm::IntegerType::get(CGM.getLLVMContext(), + CGM.getContext().toBits(unitSize)); + Entries.push_back({unitBegin, unitEnd, entryTy}); + + // The next chunk starts where this chunk left off. + begin = localEnd; + } while (begin != end); + } + + // Okay, finally finished. + Finished = true; +} + +void SwiftAggLowering::enumerateComponents(EnumerationCallback callback) const { + assert(Finished && "haven't yet finished lowering"); + + for (auto &entry : Entries) { + callback(entry.Begin, entry.Type); + } +} + +std::pair +SwiftAggLowering::getCoerceAndExpandTypes() const { + assert(Finished && "haven't yet finished lowering"); + + auto &ctx = CGM.getLLVMContext(); + + if (Entries.empty()) { + auto type = llvm::StructType::get(ctx); + return { type, type }; + } + + SmallVector elts; + CharUnits lastEnd = CharUnits::Zero(); + bool hasPadding = false; + bool packed = false; + for (auto &entry : Entries) { + if (entry.Begin != lastEnd) { + auto paddingSize = entry.Begin - lastEnd; + assert(!paddingSize.isNegative()); + + auto padding = llvm::ArrayType::get(llvm::Type::getInt8Ty(ctx), + paddingSize.getQuantity()); + elts.push_back(padding); + hasPadding = true; + } + + if (!packed && !entry.Begin.isMultipleOf( + CharUnits::fromQuantity( + CGM.getDataLayout().getABITypeAlignment(entry.Type)))) + packed = true; + + elts.push_back(entry.Type); + lastEnd = entry.End; + } + + // We don't need to adjust 'packed' to deal with possible tail padding + // because we never do that kind of access through the coercion type. + auto coercionType = llvm::StructType::get(ctx, elts, packed); + + llvm::Type *unpaddedType = coercionType; + if (hasPadding) { + elts.clear(); + for (auto &entry : Entries) { + elts.push_back(entry.Type); + } + if (elts.size() == 1) { + unpaddedType = elts[0]; + } else { + unpaddedType = llvm::StructType::get(ctx, elts, /*packed*/ false); + } + } else if (Entries.size() == 1) { + unpaddedType = Entries[0].Type; + } + + return { coercionType, unpaddedType }; +} + +bool SwiftAggLowering::shouldPassIndirectly(bool asReturnValue) const { + assert(Finished && "haven't yet finished lowering"); + + // Empty types don't need to be passed indirectly. + if (Entries.empty()) return false; + + CharUnits totalSize = Entries.back().End; + + // Avoid copying the array of types when there's just a single element. + if (Entries.size() == 1) { + return getSwiftABIInfo(CGM).shouldPassIndirectlyForSwift(totalSize, + Entries.back().Type, + asReturnValue); + } + + SmallVector componentTys; + componentTys.reserve(Entries.size()); + for (auto &entry : Entries) { + componentTys.push_back(entry.Type); + } + return getSwiftABIInfo(CGM).shouldPassIndirectlyForSwift(totalSize, + componentTys, + asReturnValue); +} + +CharUnits swiftcall::getMaximumVoluntaryIntegerSize(CodeGenModule &CGM) { + // Currently always the size of an ordinary pointer. + return CGM.getContext().toCharUnitsFromBits( + CGM.getContext().getTargetInfo().getPointerWidth(0)); +} + +CharUnits swiftcall::getNaturalAlignment(CodeGenModule &CGM, llvm::Type *type) { + // For Swift's purposes, this is always just the store size of the type + // rounded up to a power of 2. + auto size = (unsigned long long) getTypeStoreSize(CGM, type).getQuantity(); + if (!isPowerOf2(size)) { + size = 1U << (llvm::findLastSet(size, llvm::ZB_Undefined) + 1); + } + assert(size >= CGM.getDataLayout().getABITypeAlignment(type)); + return CharUnits::fromQuantity(size); +} + +bool swiftcall::isLegalIntegerType(CodeGenModule &CGM, + llvm::IntegerType *intTy) { + auto size = intTy->getBitWidth(); + switch (size) { + case 1: + case 8: + case 16: + case 32: + case 64: + // Just assume that the above are always legal. + return true; + + case 128: + return CGM.getContext().getTargetInfo().hasInt128Type(); + + default: + return false; + } +} + +bool swiftcall::isLegalVectorType(CodeGenModule &CGM, CharUnits vectorSize, + llvm::VectorType *vectorTy) { + return isLegalVectorType(CGM, vectorSize, vectorTy->getElementType(), + vectorTy->getNumElements()); +} + +bool swiftcall::isLegalVectorType(CodeGenModule &CGM, CharUnits vectorSize, + llvm::Type *eltTy, unsigned numElts) { + assert(numElts > 1 && "illegal vector length"); + return getSwiftABIInfo(CGM) + .isLegalVectorTypeForSwift(vectorSize, eltTy, numElts); +} + +std::pair +swiftcall::splitLegalVectorType(CodeGenModule &CGM, CharUnits vectorSize, + llvm::VectorType *vectorTy) { + auto numElts = vectorTy->getNumElements(); + auto eltTy = vectorTy->getElementType(); + + // Try to split the vector type in half. + if (numElts >= 4 && isPowerOf2(numElts)) { + if (isLegalVectorType(CGM, vectorSize / 2, eltTy, numElts / 2)) + return {llvm::VectorType::get(eltTy, numElts / 2), 2}; + } + + return {eltTy, numElts}; +} + +void swiftcall::legalizeVectorType(CodeGenModule &CGM, CharUnits origVectorSize, + llvm::VectorType *origVectorTy, + llvm::SmallVectorImpl &components) { + // If it's already a legal vector type, use it. + if (isLegalVectorType(CGM, origVectorSize, origVectorTy)) { + components.push_back(origVectorTy); + return; + } + + // Try to split the vector into legal subvectors. + auto numElts = origVectorTy->getNumElements(); + auto eltTy = origVectorTy->getElementType(); + assert(numElts != 1); + + // The largest size that we're still considering making subvectors of. + // Always a power of 2. + unsigned logCandidateNumElts = llvm::findLastSet(numElts, llvm::ZB_Undefined); + unsigned candidateNumElts = 1U << logCandidateNumElts; + assert(candidateNumElts <= numElts && candidateNumElts * 2 > numElts); + + // Minor optimization: don't check the legality of this exact size twice. + if (candidateNumElts == numElts) { + logCandidateNumElts--; + candidateNumElts >>= 1; + } + + CharUnits eltSize = (origVectorSize / numElts); + CharUnits candidateSize = eltSize * candidateNumElts; + + // The sensibility of this algorithm relies on the fact that we never + // have a legal non-power-of-2 vector size without having the power of 2 + // also be legal. + while (logCandidateNumElts > 0) { + assert(candidateNumElts == 1U << logCandidateNumElts); + assert(candidateNumElts <= numElts); + assert(candidateSize == eltSize * candidateNumElts); + + // Skip illegal vector sizes. + if (!isLegalVectorType(CGM, candidateSize, eltTy, candidateNumElts)) { + logCandidateNumElts--; + candidateNumElts /= 2; + candidateSize /= 2; + continue; + } + + // Add the right number of vectors of this size. + auto numVecs = numElts >> logCandidateNumElts; + components.append(numVecs, llvm::VectorType::get(eltTy, candidateNumElts)); + numElts -= (numVecs << logCandidateNumElts); + + if (numElts == 0) return; + + // It's possible that the number of elements remaining will be legal. + // This can happen with e.g. <7 x float> when <3 x float> is legal. + // This only needs to be separately checked if it's not a power of 2. + if (numElts > 2 && !isPowerOf2(numElts) && + isLegalVectorType(CGM, eltSize * numElts, eltTy, numElts)) { + components.push_back(llvm::VectorType::get(eltTy, numElts)); + return; + } + + // Bring vecSize down to something no larger than numElts. + do { + logCandidateNumElts--; + candidateNumElts /= 2; + candidateSize /= 2; + } while (candidateNumElts > numElts); + } + + // Otherwise, just append a bunch of individual elements. + components.append(numElts, eltTy); +} + +bool swiftcall::shouldPassCXXRecordIndirectly(CodeGenModule &CGM, + const CXXRecordDecl *record) { + // Following a recommendation from Richard Smith, pass a C++ type + // indirectly only if the destructor is non-trivial or *all* of the + // copy/move constructors are deleted or non-trivial. + + if (record->hasNonTrivialDestructor()) + return true; + + // It would be nice if this were summarized on the CXXRecordDecl. + for (auto ctor : record->ctors()) { + if (ctor->isCopyOrMoveConstructor() && !ctor->isDeleted() && + ctor->isTrivial()) { + return false; + } + } + + return true; +} + +static ABIArgInfo classifyExpandedType(SwiftAggLowering &lowering, + bool forReturn, + CharUnits alignmentForIndirect) { + if (lowering.empty()) { + return ABIArgInfo::getIgnore(); + } else if (lowering.shouldPassIndirectly(forReturn)) { + return ABIArgInfo::getIndirect(alignmentForIndirect, /*byval*/ false); + } else { + auto types = lowering.getCoerceAndExpandTypes(); + return ABIArgInfo::getCoerceAndExpand(types.first, types.second); + } +} + +static ABIArgInfo classifyType(CodeGenModule &CGM, CanQualType type, + bool forReturn) { + if (auto recordType = dyn_cast(type)) { + auto record = recordType->getDecl(); + auto &layout = CGM.getContext().getASTRecordLayout(record); + + if (auto cxxRecord = dyn_cast(record)) { + if (shouldPassCXXRecordIndirectly(CGM, cxxRecord)) + return ABIArgInfo::getIndirect(layout.getAlignment(), /*byval*/ false); + } + + SwiftAggLowering lowering(CGM); + lowering.addTypedData(recordType->getDecl(), CharUnits::Zero(), layout); + lowering.finish(); + + return classifyExpandedType(lowering, forReturn, layout.getAlignment()); + } + + // Just assume that all of our target ABIs can support returning at least + // two integer or floating-point values. + if (isa(type)) { + return (forReturn ? ABIArgInfo::getDirect() : ABIArgInfo::getExpand()); + } + + // Vector types may need to be legalized. + if (isa(type)) { + SwiftAggLowering lowering(CGM); + lowering.addTypedData(type, CharUnits::Zero()); + lowering.finish(); + + CharUnits alignment = CGM.getContext().getTypeAlignInChars(type); + return classifyExpandedType(lowering, forReturn, alignment); + } + + // Member pointer types need to be expanded, but it's a simple form of + // expansion that 'Direct' can handle. Note that CanBeFlattened should be + // true for this to work. + + // 'void' needs to be ignored. + if (type->isVoidType()) { + return ABIArgInfo::getIgnore(); + } + + // Everything else can be passed directly. + return ABIArgInfo::getDirect(); +} + +ABIArgInfo swiftcall::classifyReturnType(CodeGenModule &CGM, CanQualType type) { + return classifyType(CGM, type, /*forReturn*/ true); +} + +ABIArgInfo swiftcall::classifyArgumentType(CodeGenModule &CGM, + CanQualType type) { + return classifyType(CGM, type, /*forReturn*/ false); +} + +void swiftcall::computeABIInfo(CodeGenModule &CGM, CGFunctionInfo &FI) { + auto &retInfo = FI.getReturnInfo(); + retInfo = classifyReturnType(CGM, FI.getReturnType()); + + for (unsigned i = 0, e = FI.arg_size(); i != e; ++i) { + auto &argInfo = FI.arg_begin()[i]; + argInfo.info = classifyArgumentType(CGM, argInfo.type); + } +} \ No newline at end of file diff --git a/lib/CodeGen/TargetInfo.cpp b/lib/CodeGen/TargetInfo.cpp index 73c146ee850..f7237b68c60 100644 --- a/lib/CodeGen/TargetInfo.cpp +++ b/lib/CodeGen/TargetInfo.cpp @@ -19,6 +19,7 @@ #include "CodeGenFunction.h" #include "clang/AST/RecordLayout.h" #include "clang/CodeGen/CGFunctionInfo.h" +#include "clang/CodeGen/SwiftCallingConv.h" #include "clang/Frontend/CodeGenOptions.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Triple.h" @@ -68,6 +69,46 @@ Address ABIInfo::EmitMSVAArg(CodeGenFunction &CGF, Address VAListAddr, ABIInfo::~ABIInfo() {} +/// Does the given lowering require more than the given number of +/// registers when expanded? +/// +/// This is intended to be the basis of a reasonable basic implementation +/// of should{Pass,Return}IndirectlyForSwift. +/// +/// For most targets, a limit of four total registers is reasonable; this +/// limits the amount of code required in order to move around the value +/// in case it wasn't produced immediately prior to the call by the caller +/// (or wasn't produced in exactly the right registers) or isn't used +/// immediately within the callee. But some targets may need to further +/// limit the register count due to an inability to support that many +/// return registers. +static bool occupiesMoreThan(CodeGenTypes &cgt, + ArrayRef scalarTypes, + unsigned maxAllRegisters) { + unsigned intCount = 0, fpCount = 0; + for (llvm::Type *type : scalarTypes) { + if (type->isPointerTy()) { + intCount++; + } else if (auto intTy = dyn_cast(type)) { + auto ptrWidth = cgt.getTarget().getPointerWidth(0); + intCount += (intTy->getBitWidth() + ptrWidth - 1) / ptrWidth; + } else { + assert(type->isVectorTy() || type->isFloatingPointTy()); + fpCount++; + } + } + + return (intCount + fpCount > maxAllRegisters); +} + +bool SwiftABIInfo::isLegalVectorTypeForSwift(CharUnits vectorSize, + llvm::Type *eltTy, + unsigned numElts) const { + // The default implementation of this assumes that the target guarantees + // 128-bit SIMD support but nothing more. + return (vectorSize.getQuantity() > 8 && vectorSize.getQuantity() <= 16); +} + static CGCXXABI::RecordArgABI getRecordArgABI(const RecordType *RT, CGCXXABI &CXXABI) { const CXXRecordDecl *RD = dyn_cast(RT->getDecl()); @@ -801,7 +842,7 @@ struct CCState { }; /// X86_32ABIInfo - The X86-32 ABI information. -class X86_32ABIInfo : public ABIInfo { +class X86_32ABIInfo : public SwiftABIInfo { enum Class { Integer, Float @@ -870,12 +911,22 @@ class X86_32ABIInfo : public ABIInfo { X86_32ABIInfo(CodeGen::CodeGenTypes &CGT, bool DarwinVectorABI, bool RetSmallStructInRegABI, bool Win32StructABI, unsigned NumRegisterParameters, bool SoftFloatABI) - : ABIInfo(CGT), IsDarwinVectorABI(DarwinVectorABI), + : SwiftABIInfo(CGT), IsDarwinVectorABI(DarwinVectorABI), IsRetSmallStructInRegABI(RetSmallStructInRegABI), IsWin32StructABI(Win32StructABI), IsSoftFloatABI(SoftFloatABI), IsMCUABI(CGT.getTarget().getTriple().isOSIAMCU()), DefaultNumRegisterParameters(NumRegisterParameters) {} + + bool shouldPassIndirectlyForSwift(CharUnits totalSize, + ArrayRef scalars, + bool asReturnValue) const override { + // LLVM's x86-32 lowering currently only assigns up to three + // integer registers and three fp registers. Oddly, it'll use up to + // four vector registers for vectors, but those can overlap with the + // scalar registers. + return occupiesMoreThan(CGT, scalars, /*total*/ 3); + } }; class X86_32TargetCodeGenInfo : public TargetCodeGenInfo { @@ -1689,7 +1740,7 @@ static unsigned getNativeVectorSizeForAVXABI(X86AVXABILevel AVXLevel) { } /// X86_64ABIInfo - The X86_64 ABI information. -class X86_64ABIInfo : public ABIInfo { +class X86_64ABIInfo : public SwiftABIInfo { enum Class { Integer = 0, SSE, @@ -1800,7 +1851,7 @@ class X86_64ABIInfo : public ABIInfo { public: X86_64ABIInfo(CodeGen::CodeGenTypes &CGT, X86AVXABILevel AVXLevel) : - ABIInfo(CGT), AVXLevel(AVXLevel), + SwiftABIInfo(CGT), AVXLevel(AVXLevel), Has64BitPointers(CGT.getDataLayout().getPointerSize(0) == 8) { } @@ -1827,6 +1878,12 @@ class X86_64ABIInfo : public ABIInfo { bool has64BitPointers() const { return Has64BitPointers; } + + bool shouldPassIndirectlyForSwift(CharUnits totalSize, + ArrayRef scalars, + bool asReturnValue) const override { + return occupiesMoreThan(CGT, scalars, /*total*/ 4); + } }; /// WinX86_64ABIInfo - The Windows X86_64 ABI information. @@ -4248,7 +4305,7 @@ PPC64TargetCodeGenInfo::initDwarfEHRegSizeTable(CodeGen::CodeGenFunction &CGF, namespace { -class AArch64ABIInfo : public ABIInfo { +class AArch64ABIInfo : public SwiftABIInfo { public: enum ABIKind { AAPCS = 0, @@ -4259,7 +4316,8 @@ class AArch64ABIInfo : public ABIInfo { ABIKind Kind; public: - AArch64ABIInfo(CodeGenTypes &CGT, ABIKind Kind) : ABIInfo(CGT), Kind(Kind) {} + AArch64ABIInfo(CodeGenTypes &CGT, ABIKind Kind) + : SwiftABIInfo(CGT), Kind(Kind) {} private: ABIKind getABIKind() const { return Kind; } @@ -4292,6 +4350,12 @@ class AArch64ABIInfo : public ABIInfo { return isDarwinPCS() ? EmitDarwinVAArg(VAListAddr, Ty, CGF) : EmitAAPCSVAArg(VAListAddr, Ty, CGF); } + + bool shouldPassIndirectlyForSwift(CharUnits totalSize, + ArrayRef scalars, + bool asReturnValue) const override { + return occupiesMoreThan(CGT, scalars, /*total*/ 4); + } }; class AArch64TargetCodeGenInfo : public TargetCodeGenInfo { @@ -4761,7 +4825,7 @@ Address AArch64ABIInfo::EmitDarwinVAArg(Address VAListAddr, QualType Ty, namespace { -class ARMABIInfo : public ABIInfo { +class ARMABIInfo : public SwiftABIInfo { public: enum ABIKind { APCS = 0, @@ -4774,7 +4838,8 @@ class ARMABIInfo : public ABIInfo { ABIKind Kind; public: - ARMABIInfo(CodeGenTypes &CGT, ABIKind _Kind) : ABIInfo(CGT), Kind(_Kind) { + ARMABIInfo(CodeGenTypes &CGT, ABIKind _Kind) + : SwiftABIInfo(CGT), Kind(_Kind) { setCCs(); } @@ -4825,6 +4890,12 @@ class ARMABIInfo : public ABIInfo { llvm::CallingConv::ID getLLVMDefaultCC() const; llvm::CallingConv::ID getABIDefaultCC() const; void setCCs(); + + bool shouldPassIndirectlyForSwift(CharUnits totalSize, + ArrayRef scalars, + bool asReturnValue) const override { + return occupiesMoreThan(CGT, scalars, /*total*/ 4); + } }; class ARMTargetCodeGenInfo : public TargetCodeGenInfo { diff --git a/lib/CodeGen/TargetInfo.h b/lib/CodeGen/TargetInfo.h index 87b47049862..71f6b0a4c5c 100644 --- a/lib/CodeGen/TargetInfo.h +++ b/lib/CodeGen/TargetInfo.h @@ -29,15 +29,14 @@ class Value; } namespace clang { -class ABIInfo; class Decl; namespace CodeGen { +class ABIInfo; class CallArgList; class CodeGenModule; class CodeGenFunction; class CGFunctionInfo; -} /// TargetCodeGenInfo - This class organizes various target-specific /// codegeneration issues, like target-specific attributes, builtins and so @@ -219,6 +218,8 @@ class TargetCodeGenInfo { llvm::StringRef Value, llvm::SmallString<32> &Opt) const {} }; + +} // namespace CodeGen } // namespace clang #endif // LLVM_CLANG_LIB_CODEGEN_TARGETINFO_H diff --git a/test/CodeGen/arm-swiftcall.c b/test/CodeGen/arm-swiftcall.c new file mode 100644 index 00000000000..dc1d68e34b9 --- /dev/null +++ b/test/CodeGen/arm-swiftcall.c @@ -0,0 +1,496 @@ +// RUN: %clang_cc1 -triple armv7-apple-darwin9 -emit-llvm -o - %s | FileCheck %s + +// This isn't really testing anything ARM-specific; it's just a convenient +// 32-bit platform. + +#define SWIFTCALL __attribute__((swiftcall)) +#define OUT __attribute__((swift_indirect_result)) +#define ERROR __attribute__((swift_error_result)) +#define CONTEXT __attribute__((swift_context)) + +/*****************************************************************************/ +/****************************** PARAMETER ABIS *******************************/ +/*****************************************************************************/ + +SWIFTCALL void indirect_result_1(OUT int *arg0, OUT float *arg1) {} +// CHECK-LABEL: define {{.*}} void @indirect_result_1(i32* noalias sret align 4 dereferenceable(4){{.*}}, float* noalias align 4 dereferenceable(4){{.*}}) + +// TODO: maybe this shouldn't suppress sret. +SWIFTCALL int indirect_result_2(OUT int *arg0, OUT float *arg1) { __builtin_unreachable(); } +// CHECK-LABEL: define {{.*}} i32 @indirect_result_2(i32* noalias align 4 dereferenceable(4){{.*}}, float* noalias align 4 dereferenceable(4){{.*}}) + +typedef struct { char array[1024]; } struct_reallybig; +SWIFTCALL struct_reallybig indirect_result_3(OUT int *arg0, OUT float *arg1) { __builtin_unreachable(); } +// CHECK-LABEL: define {{.*}} void @indirect_result_3({{.*}}* noalias sret {{.*}}, i32* noalias align 4 dereferenceable(4){{.*}}, float* noalias align 4 dereferenceable(4){{.*}}) + +SWIFTCALL void context_1(CONTEXT void *self) {} +// CHECK-LABEL: define {{.*}} void @context_1(i8* swiftself + +SWIFTCALL void context_2(void *arg0, CONTEXT void *self) {} +// CHECK-LABEL: define {{.*}} void @context_2(i8*{{.*}}, i8* swiftself + +SWIFTCALL void context_error_1(CONTEXT int *self, ERROR float **error) {} +// CHECK-LABEL: define {{.*}} void @context_error_1(i32* swiftself{{.*}}, float** swifterror) +// CHECK: [[TEMP:%.*]] = alloca float*, align 4 +// CHECK: [[T0:%.*]] = load float*, float** [[ERRORARG:%.*]], align 4 +// CHECK: store float* [[T0]], float** [[TEMP]], align 4 +// CHECK: [[T0:%.*]] = load float*, float** [[TEMP]], align 4 +// CHECK: store float* [[T0]], float** [[ERRORARG]], align 4 +void test_context_error_1() { + int x; + float *error; + context_error_1(&x, &error); +} +// CHECK-LABEL: define void @test_context_error_1() +// CHECK: [[X:%.*]] = alloca i32, align 4 +// CHECK: [[ERROR:%.*]] = alloca float*, align 4 +// CHECK: [[TEMP:%.*]] = alloca swifterror float*, align 4 +// CHECK: [[T0:%.*]] = load float*, float** [[ERROR]], align 4 +// CHECK: store float* [[T0]], float** [[TEMP]], align 4 +// CHECK: call [[SWIFTCC:swiftcc]] void @context_error_1(i32* swiftself [[X]], float** swifterror [[TEMP]]) +// CHECK: [[T0:%.*]] = load float*, float** [[TEMP]], align 4 +// CHECK: store float* [[T0]], float** [[ERROR]], align 4 + +SWIFTCALL void context_error_2(short s, CONTEXT int *self, ERROR float **error) {} +// CHECK-LABEL: define {{.*}} void @context_error_2(i16{{.*}}, i32* swiftself{{.*}}, float** swifterror) + +/*****************************************************************************/ +/********************************** LOWERING *********************************/ +/*****************************************************************************/ + +typedef float float4 __attribute__((ext_vector_type(4))); +typedef float float8 __attribute__((ext_vector_type(8))); +typedef double double2 __attribute__((ext_vector_type(2))); +typedef double double4 __attribute__((ext_vector_type(4))); +typedef int int4 __attribute__((ext_vector_type(4))); +typedef int int5 __attribute__((ext_vector_type(5))); +typedef int int8 __attribute__((ext_vector_type(8))); + +#define TEST(TYPE) \ + SWIFTCALL TYPE return_##TYPE(void) { \ + TYPE result = {}; \ + return result; \ + } \ + SWIFTCALL void take_##TYPE(TYPE v) { \ + } \ + void test_##TYPE() { \ + take_##TYPE(return_##TYPE()); \ + } + +/*****************************************************************************/ +/*********************************** STRUCTS *********************************/ +/*****************************************************************************/ + +typedef struct { +} struct_empty; +TEST(struct_empty); +// CHECK-LABEL: define {{.*}} @return_struct_empty() +// CHECK: ret void +// CHECK-LABEL: define {{.*}} @take_struct_empty() +// CHECK: ret void + +typedef struct { + int x; + char c0; + char c1; + float f0; + float f1; +} struct_1; +TEST(struct_1); +// CHECK-LABEL: define {{.*}} @return_struct_1() +// CHECK: [[RET:%.*]] = alloca [[REC:%.*]], align 4 +// CHECK: [[VAR:%.*]] = alloca [[REC]], align 4 +// CHECK: @llvm.memset +// CHECK: @llvm.memcpy +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[RET]] to [[AGG:{ i32, i16, \[2 x i8\], float, float }]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load i32, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[SECOND:%.*]] = load i16, i16* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 3 +// CHECK: [[THIRD:%.*]] = load float, float* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 4 +// CHECK: [[FOURTH:%.*]] = load float, float* [[T0]], align +// CHECK: [[T0:%.*]] = insertvalue [[UAGG:{ i32, i16, float, float }]] undef, i32 [[FIRST]], 0 +// CHECK: [[T1:%.*]] = insertvalue [[UAGG]] [[T0]], i16 [[SECOND]], 1 +// CHECK: [[T2:%.*]] = insertvalue [[UAGG]] [[T1]], float [[THIRD]], 2 +// CHECK: [[T3:%.*]] = insertvalue [[UAGG]] [[T2]], float [[FOURTH]], 3 +// CHECK: ret [[UAGG]] [[T3]] +// CHECK-LABEL: define {{.*}} @take_struct_1(i32, i16, float, float) +// CHECK: [[V:%.*]] = alloca [[REC]], align 4 +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[V]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: store i32 %0, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: store i16 %1, i16* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 3 +// CHECK: store float %2, float* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 4 +// CHECK: store float %3, float* [[T0]], align 4 +// CHECK: ret void +// CHECK-LABEL: define void @test_struct_1() +// CHECK: [[TMP:%.*]] = alloca [[REC]], align 4 +// CHECK: [[CALL:%.*]] = call [[SWIFTCC]] [[UAGG]] @return_struct_1() +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 0 +// CHECK: store i32 [[T1]], i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 1 +// CHECK: store i16 [[T1]], i16* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 3 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 2 +// CHECK: store float [[T1]], float* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 4 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 3 +// CHECK: store float [[T1]], float* [[T0]], align 4 +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load i32, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[SECOND:%.*]] = load i16, i16* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 3 +// CHECK: [[THIRD:%.*]] = load float, float* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 4 +// CHECK: [[FOURTH:%.*]] = load float, float* [[T0]], align 4 +// CHECK: call [[SWIFTCC]] void @take_struct_1(i32 [[FIRST]], i16 [[SECOND]], float [[THIRD]], float [[FOURTH]]) +// CHECK: ret void + +typedef struct { + int x; + char c0; + __attribute__((aligned(2))) char c1; + float f0; + float f1; +} struct_2; +TEST(struct_2); +// CHECK-LABEL: define {{.*}} @return_struct_2() +// CHECK: [[RET:%.*]] = alloca [[REC:%.*]], align 4 +// CHECK: [[VAR:%.*]] = alloca [[REC]], align 4 +// CHECK: @llvm.memcpy +// CHECK: @llvm.memcpy +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[RET]] to [[AGG:{ i32, i32, float, float }]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load i32, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[SECOND:%.*]] = load i32, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 2 +// CHECK: [[THIRD:%.*]] = load float, float* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 3 +// CHECK: [[FOURTH:%.*]] = load float, float* [[T0]], align +// CHECK: [[T0:%.*]] = insertvalue [[UAGG:{ i32, i32, float, float }]] undef, i32 [[FIRST]], 0 +// CHECK: [[T1:%.*]] = insertvalue [[UAGG]] [[T0]], i32 [[SECOND]], 1 +// CHECK: [[T2:%.*]] = insertvalue [[UAGG]] [[T1]], float [[THIRD]], 2 +// CHECK: [[T3:%.*]] = insertvalue [[UAGG]] [[T2]], float [[FOURTH]], 3 +// CHECK: ret [[UAGG]] [[T3]] +// CHECK-LABEL: define {{.*}} @take_struct_2(i32, i32, float, float) +// CHECK: [[V:%.*]] = alloca [[REC]], align 4 +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[V]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: store i32 %0, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: store i32 %1, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 2 +// CHECK: store float %2, float* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 3 +// CHECK: store float %3, float* [[T0]], align 4 +// CHECK: ret void +// CHECK-LABEL: define void @test_struct_2() +// CHECK: [[TMP:%.*]] = alloca [[REC]], align 4 +// CHECK: [[CALL:%.*]] = call [[SWIFTCC]] [[UAGG]] @return_struct_2() +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 0 +// CHECK: store i32 [[T1]], i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 1 +// CHECK: store i32 [[T1]], i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 2 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 2 +// CHECK: store float [[T1]], float* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 3 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 3 +// CHECK: store float [[T1]], float* [[T0]], align 4 +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load i32, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[SECOND:%.*]] = load i32, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 2 +// CHECK: [[THIRD:%.*]] = load float, float* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 3 +// CHECK: [[FOURTH:%.*]] = load float, float* [[T0]], align 4 +// CHECK: call [[SWIFTCC]] void @take_struct_2(i32 [[FIRST]], i32 [[SECOND]], float [[THIRD]], float [[FOURTH]]) +// CHECK: ret void + +// There's no way to put a field randomly in the middle of an otherwise +// empty storage unit in C, so that case has to be tested in C++, which +// can use empty structs to introduce arbitrary padding. (In C, they end up +// with size 0 and so don't affect layout.) + +// Misaligned data rule. +typedef struct { + char c0; + __attribute__((packed)) float f; +} struct_misaligned_1; +TEST(struct_misaligned_1) +// CHECK-LABEL: define {{.*}} @return_struct_misaligned_1() +// CHECK: [[RET:%.*]] = alloca [[REC:%.*]], align +// CHECK: [[VAR:%.*]] = alloca [[REC]], align +// CHECK: @llvm.memset +// CHECK: @llvm.memcpy +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[RET]] to [[AGG:{ i32, i8 }]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load i32, i32* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[SECOND:%.*]] = load i8, i8* [[T0]], align +// CHECK: [[T0:%.*]] = insertvalue [[UAGG:{ i32, i8 }]] undef, i32 [[FIRST]], 0 +// CHECK: [[T1:%.*]] = insertvalue [[UAGG]] [[T0]], i8 [[SECOND]], 1 +// CHECK: ret [[UAGG]] [[T1]] +// CHECK-LABEL: define {{.*}} @take_struct_misaligned_1(i32, i8) +// CHECK: [[V:%.*]] = alloca [[REC]], align +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[V]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: store i32 %0, i32* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: store i8 %1, i8* [[T0]], align +// CHECK: ret void + +// Too many scalars. +typedef struct { + int x[5]; +} struct_big_1; +TEST(struct_big_1) + +// CHECK-LABEL: define {{.*}} void @return_struct_big_1({{.*}} noalias sret + +// Should not be byval. +// CHECK-LABEL: define {{.*}} void @take_struct_big_1({{.*}}*{{( %.*)?}}) + +/*****************************************************************************/ +/********************************* TYPE MERGING ******************************/ +/*****************************************************************************/ + +typedef union { + float f; + double d; +} union_het_fp; +TEST(union_het_fp) +// CHECK-LABEL: define {{.*}} @return_union_het_fp() +// CHECK: [[RET:%.*]] = alloca [[REC:%.*]], align 4 +// CHECK: [[VAR:%.*]] = alloca [[REC]], align 4 +// CHECK: @llvm.memcpy +// CHECK: @llvm.memcpy +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[RET]] to [[AGG:{ i32, i32 }]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load i32, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[SECOND:%.*]] = load i32, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = insertvalue [[UAGG:{ i32, i32 }]] undef, i32 [[FIRST]], 0 +// CHECK: [[T1:%.*]] = insertvalue [[UAGG]] [[T0]], i32 [[SECOND]], 1 +// CHECK: ret [[UAGG]] [[T1]] +// CHECK-LABEL: define {{.*}} @take_union_het_fp(i32, i32) +// CHECK: [[V:%.*]] = alloca [[REC]], align 4 +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[V]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: store i32 %0, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: store i32 %1, i32* [[T0]], align 4 +// CHECK: ret void +// CHECK-LABEL: define void @test_union_het_fp() +// CHECK: [[TMP:%.*]] = alloca [[REC]], align 4 +// CHECK: [[CALL:%.*]] = call [[SWIFTCC]] [[UAGG]] @return_union_het_fp() +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 0 +// CHECK: store i32 [[T1]], i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 1 +// CHECK: store i32 [[T1]], i32* [[T0]], align 4 +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load i32, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[SECOND:%.*]] = load i32, i32* [[T0]], align 4 +// CHECK: call [[SWIFTCC]] void @take_union_het_fp(i32 [[FIRST]], i32 [[SECOND]]) +// CHECK: ret void + + +typedef union { + float f1; + float f2; +} union_hom_fp; +TEST(union_hom_fp) +// CHECK-LABEL: define void @test_union_hom_fp() +// CHECK: [[TMP:%.*]] = alloca [[REC:%.*]], align 4 +// CHECK: [[CALL:%.*]] = call [[SWIFTCC]] float @return_union_hom_fp() +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP]] to [[AGG:{ float }]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: store float [[CALL]], float* [[T0]], align 4 +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load float, float* [[T0]], align 4 +// CHECK: call [[SWIFTCC]] void @take_union_hom_fp(float [[FIRST]]) +// CHECK: ret void + +typedef union { + float f1; + float4 fv2; +} union_hom_fp_partial; +TEST(union_hom_fp_partial) +// CHECK-LABEL: define void @test_union_hom_fp_partial() +// CHECK: [[TMP:%.*]] = alloca [[REC:%.*]], align 16 +// CHECK: [[CALL:%.*]] = call [[SWIFTCC]] [[UAGG:{ float, float, float, float }]] @return_union_hom_fp_partial() +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP]] to [[AGG:{ float, float, float, float }]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 0 +// CHECK: store float [[T1]], float* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 1 +// CHECK: store float [[T1]], float* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 2 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 2 +// CHECK: store float [[T1]], float* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 3 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 3 +// CHECK: store float [[T1]], float* [[T0]], align +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load float, float* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[SECOND:%.*]] = load float, float* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 2 +// CHECK: [[THIRD:%.*]] = load float, float* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 3 +// CHECK: [[FOURTH:%.*]] = load float, float* [[T0]], align +// CHECK: call [[SWIFTCC]] void @take_union_hom_fp_partial(float [[FIRST]], float [[SECOND]], float [[THIRD]], float [[FOURTH]]) +// CHECK: ret void + +typedef union { + struct { int x, y; } f1; + float4 fv2; +} union_het_fpv_partial; +TEST(union_het_fpv_partial) +// CHECK-LABEL: define void @test_union_het_fpv_partial() +// CHECK: [[TMP:%.*]] = alloca [[REC:%.*]], align 16 +// CHECK: [[CALL:%.*]] = call [[SWIFTCC]] [[UAGG:{ i32, i32, float, float }]] @return_union_het_fpv_partial() +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP]] to [[AGG:{ i32, i32, float, float }]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 0 +// CHECK: store i32 [[T1]], i32* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 1 +// CHECK: store i32 [[T1]], i32* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 2 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 2 +// CHECK: store float [[T1]], float* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 3 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 3 +// CHECK: store float [[T1]], float* [[T0]], align +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load i32, i32* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[SECOND:%.*]] = load i32, i32* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 2 +// CHECK: [[THIRD:%.*]] = load float, float* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 3 +// CHECK: [[FOURTH:%.*]] = load float, float* [[T0]], align +// CHECK: call [[SWIFTCC]] void @take_union_het_fpv_partial(i32 [[FIRST]], i32 [[SECOND]], float [[THIRD]], float [[FOURTH]]) +// CHECK: ret void + +/*****************************************************************************/ +/****************************** VECTOR LEGALIZATION **************************/ +/*****************************************************************************/ + +TEST(int4) +// CHECK-LABEL: define {{.*}} <4 x i32> @return_int4() +// CHECK-LABEL: define {{.*}} @take_int4(<4 x i32> + +TEST(int8) +// CHECK-LABEL: define {{.*}} @return_int8() +// CHECK: [[RET:%.*]] = alloca [[REC:<8 x i32>]], align 32 +// CHECK: [[VAR:%.*]] = alloca [[REC]], align +// CHECK: store +// CHECK: load +// CHECK: store +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[RET]] to [[AGG:{ <4 x i32>, <4 x i32> }]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load <4 x i32>, <4 x i32>* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[SECOND:%.*]] = load <4 x i32>, <4 x i32>* [[T0]], align +// CHECK: [[T0:%.*]] = insertvalue [[UAGG:{ <4 x i32>, <4 x i32> }]] undef, <4 x i32> [[FIRST]], 0 +// CHECK: [[T1:%.*]] = insertvalue [[UAGG]] [[T0]], <4 x i32> [[SECOND]], 1 +// CHECK: ret [[UAGG]] [[T1]] +// CHECK-LABEL: define {{.*}} @take_int8(<4 x i32>, <4 x i32>) +// CHECK: [[V:%.*]] = alloca [[REC]], align +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[V]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: store <4 x i32> %0, <4 x i32>* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: store <4 x i32> %1, <4 x i32>* [[T0]], align +// CHECK: ret void +// CHECK-LABEL: define void @test_int8() +// CHECK: [[TMP1:%.*]] = alloca [[REC]], align +// CHECK: [[TMP2:%.*]] = alloca [[REC]], align +// CHECK: [[CALL:%.*]] = call [[SWIFTCC]] [[UAGG]] @return_int8() +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP1]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 0 +// CHECK: store <4 x i32> [[T1]], <4 x i32>* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 1 +// CHECK: store <4 x i32> [[T1]], <4 x i32>* [[T0]], align +// CHECK: [[V:%.*]] = load [[REC]], [[REC]]* [[TMP1]], align +// CHECK: store [[REC]] [[V]], [[REC]]* [[TMP2]], align +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP2]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load <4 x i32>, <4 x i32>* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[SECOND:%.*]] = load <4 x i32>, <4 x i32>* [[T0]], align +// CHECK: call [[SWIFTCC]] void @take_int8(<4 x i32> [[FIRST]], <4 x i32> [[SECOND]]) +// CHECK: ret void + +TEST(int5) +// CHECK-LABEL: define {{.*}} @return_int5() +// CHECK: [[RET:%.*]] = alloca [[REC:<5 x i32>]], align 32 +// CHECK: [[VAR:%.*]] = alloca [[REC]], align +// CHECK: store +// CHECK: load +// CHECK: store +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[RET]] to [[AGG:{ <4 x i32>, i32 }]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load <4 x i32>, <4 x i32>* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[SECOND:%.*]] = load i32, i32* [[T0]], align +// CHECK: [[T0:%.*]] = insertvalue [[UAGG:{ <4 x i32>, i32 }]] undef, <4 x i32> [[FIRST]], 0 +// CHECK: [[T1:%.*]] = insertvalue [[UAGG]] [[T0]], i32 [[SECOND]], 1 +// CHECK: ret [[UAGG]] [[T1]] +// CHECK-LABEL: define {{.*}} @take_int5(<4 x i32>, i32) +// CHECK: [[V:%.*]] = alloca [[REC]], align +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[V]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: store <4 x i32> %0, <4 x i32>* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: store i32 %1, i32* [[T0]], align +// CHECK: ret void +// CHECK-LABEL: define void @test_int5() +// CHECK: [[TMP1:%.*]] = alloca [[REC]], align +// CHECK: [[TMP2:%.*]] = alloca [[REC]], align +// CHECK: [[CALL:%.*]] = call [[SWIFTCC]] [[UAGG]] @return_int5() +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP1]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 0 +// CHECK: store <4 x i32> [[T1]], <4 x i32>* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 1 +// CHECK: store i32 [[T1]], i32* [[T0]], align +// CHECK: [[V:%.*]] = load [[REC]], [[REC]]* [[TMP1]], align +// CHECK: store [[REC]] [[V]], [[REC]]* [[TMP2]], align +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP2]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load <4 x i32>, <4 x i32>* [[T0]], align +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 1 +// CHECK: [[SECOND:%.*]] = load i32, i32* [[T0]], align +// CHECK: call [[SWIFTCC]] void @take_int5(<4 x i32> [[FIRST]], i32 [[SECOND]]) +// CHECK: ret void diff --git a/test/CodeGenCXX/arm-swiftcall.cpp b/test/CodeGenCXX/arm-swiftcall.cpp new file mode 100644 index 00000000000..535350c808d --- /dev/null +++ b/test/CodeGenCXX/arm-swiftcall.cpp @@ -0,0 +1,115 @@ +// RUN: %clang_cc1 -triple armv7-apple-darwin9 -emit-llvm -o - %s -Wno-return-type-c-linkage | FileCheck %s + +// This isn't really testing anything ARM-specific; it's just a convenient +// 32-bit platform. + +#define SWIFTCALL __attribute__((swiftcall)) +#define OUT __attribute__((swift_indirect_result)) +#define ERROR __attribute__((swift_error_result)) +#define CONTEXT __attribute__((swift_context)) + +/*****************************************************************************/ +/********************************** LOWERING *********************************/ +/*****************************************************************************/ + +#define TEST(TYPE) \ + extern "C" SWIFTCALL TYPE return_##TYPE(void) { \ + TYPE result = {}; \ + return result; \ + } \ + extern "C" SWIFTCALL void take_##TYPE(TYPE v) { \ + } \ + extern "C" void test_##TYPE() { \ + take_##TYPE(return_##TYPE()); \ + } + +/*****************************************************************************/ +/*********************************** STRUCTS *********************************/ +/*****************************************************************************/ + +typedef struct { +} struct_empty; +TEST(struct_empty); +// CHECK-LABEL: define {{.*}} @return_struct_empty() +// CHECK: ret void +// CHECK-LABEL: define {{.*}} @take_struct_empty() +// CHECK: ret void + +// This is only properly testable in C++ because it relies on empty structs +// actually taking up space in a structure without requiring any extra data +// to be passed. +typedef struct { + int x; + struct_empty padding[2]; + char c1; + float f0; + float f1; +} struct_1; +TEST(struct_1); +// CHECK-LABEL: define {{.*}} @return_struct_1() +// CHECK: [[RET:%.*]] = alloca [[REC:%.*]], align 4 +// CHECK: @llvm.memset +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[RET]] to [[AGG:{ i32, \[2 x i8\], i8, \[1 x i8\], float, float }]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load i32, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 2 +// CHECK: [[SECOND:%.*]] = load i8, i8* [[T0]], align 2 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 4 +// CHECK: [[THIRD:%.*]] = load float, float* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 5 +// CHECK: [[FOURTH:%.*]] = load float, float* [[T0]], align 4 +// CHECK: [[T0:%.*]] = insertvalue [[UAGG:{ i32, i8, float, float }]] undef, i32 [[FIRST]], 0 +// CHECK: [[T1:%.*]] = insertvalue [[UAGG]] [[T0]], i8 [[SECOND]], 1 +// CHECK: [[T2:%.*]] = insertvalue [[UAGG]] [[T1]], float [[THIRD]], 2 +// CHECK: [[T3:%.*]] = insertvalue [[UAGG]] [[T2]], float [[FOURTH]], 3 +// CHECK: ret [[UAGG]] [[T3]] +// CHECK-LABEL: define {{.*}} @take_struct_1(i32, i8, float, float) +// CHECK: [[V:%.*]] = alloca [[REC]], align 4 +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[V]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: store i32 %0, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 2 +// CHECK: store i8 %1, i8* [[T0]], align 2 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 4 +// CHECK: store float %2, float* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 5 +// CHECK: store float %3, float* [[T0]], align 4 +// CHECK: ret void +// CHECK-LABEL: define void @test_struct_1() +// CHECK: [[TMP:%.*]] = alloca [[REC]], align 4 +// CHECK: [[CALL:%.*]] = call [[SWIFTCC:swiftcc]] [[UAGG]] @return_struct_1() +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 0 +// CHECK: store i32 [[T1]], i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 2 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 1 +// CHECK: store i8 [[T1]], i8* [[T0]], align 2 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 4 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 2 +// CHECK: store float [[T1]], float* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 5 +// CHECK: [[T1:%.*]] = extractvalue [[UAGG]] [[CALL]], 3 +// CHECK: store float [[T1]], float* [[T0]], align 4 +// CHECK: [[CAST_TMP:%.*]] = bitcast [[REC]]* [[TMP]] to [[AGG]]* +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 0 +// CHECK: [[FIRST:%.*]] = load i32, i32* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 2 +// CHECK: [[SECOND:%.*]] = load i8, i8* [[T0]], align 2 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 4 +// CHECK: [[THIRD:%.*]] = load float, float* [[T0]], align 4 +// CHECK: [[T0:%.*]] = getelementptr inbounds [[AGG]], [[AGG]]* [[CAST_TMP]], i32 0, i32 5 +// CHECK: [[FOURTH:%.*]] = load float, float* [[T0]], align 4 +// CHECK: call [[SWIFTCC]] void @take_struct_1(i32 [[FIRST]], i8 [[SECOND]], float [[THIRD]], float [[FOURTH]]) +// CHECK: ret void + +struct struct_indirect_1 { + int x; + ~struct_indirect_1(); +}; +TEST(struct_indirect_1) + +// CHECK-LABEL: define {{.*}} void @return_struct_indirect_1({{.*}} noalias sret + +// Should not be byval. +// CHECK-LABEL: define {{.*}} void @take_struct_indirect_1({{.*}}*{{( %.*)?}}) From 9bdfee50e5000eef1ded04fb68cf33ef48b7ce19 Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 4 Apr 2016 18:53:01 +0000 Subject: [PATCH 470/742] Assignment operators should return by reference. Thanks to Sean Silva for pointing this out. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265328 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/CharUnits.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/clang/AST/CharUnits.h b/include/clang/AST/CharUnits.h index b2da51c23b8..564c8ec9b9e 100644 --- a/include/clang/AST/CharUnits.h +++ b/include/clang/AST/CharUnits.h @@ -149,7 +149,7 @@ namespace clang { CharUnits operator/ (QuantityType N) const { return CharUnits(Quantity / N); } - CharUnits operator/= (QuantityType N) { + CharUnits &operator/= (QuantityType N) { Quantity /= N; return *this; } From 37440d0c55efb9157c1b4664d812f631c6f4f890 Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 4 Apr 2016 20:39:50 +0000 Subject: [PATCH 471/742] Fix an unused-variable warning by using the variable in the place it was supposed to have been used. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265344 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/SwiftCallingConv.cpp | 2 +- test/CodeGen/arm-swiftcall.c | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/CodeGen/SwiftCallingConv.cpp b/lib/CodeGen/SwiftCallingConv.cpp index 6fae19f2779..44b46f62f86 100644 --- a/lib/CodeGen/SwiftCallingConv.cpp +++ b/lib/CodeGen/SwiftCallingConv.cpp @@ -239,7 +239,7 @@ void SwiftAggLowering::addLegalTypedData(llvm::Type *type, auto eltSize = (end - begin) / numElts; assert(eltSize == getTypeStoreSize(CGM, eltTy)); for (size_t i = 0, e = numElts; i != e; ++i) { - addLegalTypedData(type, begin, begin + eltSize); + addLegalTypedData(eltTy, begin, begin + eltSize); begin += eltSize; } assert(begin == end); diff --git a/test/CodeGen/arm-swiftcall.c b/test/CodeGen/arm-swiftcall.c index dc1d68e34b9..d54a3133708 100644 --- a/test/CodeGen/arm-swiftcall.c +++ b/test/CodeGen/arm-swiftcall.c @@ -62,6 +62,7 @@ typedef float float4 __attribute__((ext_vector_type(4))); typedef float float8 __attribute__((ext_vector_type(8))); typedef double double2 __attribute__((ext_vector_type(2))); typedef double double4 __attribute__((ext_vector_type(4))); +typedef int int3 __attribute__((ext_vector_type(3))); typedef int int4 __attribute__((ext_vector_type(4))); typedef int int5 __attribute__((ext_vector_type(5))); typedef int int8 __attribute__((ext_vector_type(8))); @@ -494,3 +495,10 @@ TEST(int5) // CHECK: [[SECOND:%.*]] = load i32, i32* [[T0]], align // CHECK: call [[SWIFTCC]] void @take_int5(<4 x i32> [[FIRST]], i32 [[SECOND]]) // CHECK: ret void + +typedef struct { + int x; + int3 v __attribute__((packed)); +} misaligned_int3; +TEST(misaligned_int3) +// CHECK-LABEL: define {{.*}} @take_misaligned_int3(i32, i32, i32, i32) From 4354d61dede206a1c1c2f5734740a7a9f54ba3e1 Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Wed, 6 Apr 2016 13:02:52 -0700 Subject: [PATCH 472/742] [Import as member] Default initialize ModuleOptions fields --- include/clang/APINotes/Types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/clang/APINotes/Types.h b/include/clang/APINotes/Types.h index 7b4d0647ef2..b19eea51991 100644 --- a/include/clang/APINotes/Types.h +++ b/include/clang/APINotes/Types.h @@ -476,7 +476,7 @@ class TypedefInfo : public CommonTypeInfo { /// Descripts a series of options for a module struct ModuleOptions { - bool SwiftInferImportAsMember; + bool SwiftInferImportAsMember = false; }; } // end namespace api_notes From b1a72ca781507dc18d49426c817fad9165ec4b54 Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Wed, 6 Apr 2016 13:07:47 -0700 Subject: [PATCH 473/742] [Import as member] Fix my screw up --- lib/APINotes/APINotesYAMLCompiler.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/APINotes/APINotesYAMLCompiler.cpp b/lib/APINotes/APINotesYAMLCompiler.cpp index a9c21535c8e..9d7873cee6d 100644 --- a/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/lib/APINotes/APINotesYAMLCompiler.cpp @@ -250,7 +250,7 @@ namespace { TagsSeq Tags; TypedefsSeq Typedefs; - llvm::Optional SwiftInferImportAsMember; + llvm::Optional SwiftInferImportAsMember = {llvm::None}; LLVM_ATTRIBUTE_DEPRECATED( void dump() LLVM_ATTRIBUTE_USED, @@ -774,8 +774,11 @@ namespace { Writer->addTypedef(t.Name, typedefInfo); } - if (TheModule.SwiftInferImportAsMember) - Writer->addModuleOptions({true}); + if (TheModule.SwiftInferImportAsMember) { + ModuleOptions opts; + opts.SwiftInferImportAsMember = true; + Writer->addModuleOptions(opts); + } if (!ErrorOccured) Writer->writeToStream(OS); From d5784dd32a2c647863347df65d9ccaebf94dfeb1 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Tue, 12 Apr 2016 00:53:26 +0000 Subject: [PATCH 474/742] [analyzer] Fix assertion in ReturnVisitor for body-farm synthesized getters Don't emit a path note marking the return site if the return statement does not have a valid location. This fixes an assertion failure I introduced in r265839. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@266031 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit f3d5db650c525ba2237d1a6bdaa4c698b853bbbd) --- lib/StaticAnalyzer/Core/BugReporterVisitors.cpp | 3 +++ test/Analysis/inlining/false-positive-suppression.m | 13 +++++++++++++ 2 files changed, 16 insertions(+) diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index e0f014714f7..657d33fa7a3 100644 --- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -324,6 +324,9 @@ class ReturnVisitor : public BugReporterVisitorImpl { } PathDiagnosticLocation L(Ret, BRC.getSourceManager(), StackFrame); + if (!L.isValid() || !L.asLocation().isValid()) + return nullptr; + return new PathDiagnosticEventPiece(L, Out.str()); } diff --git a/test/Analysis/inlining/false-positive-suppression.m b/test/Analysis/inlining/false-positive-suppression.m index 7be1cb8472f..d9678206c7c 100644 --- a/test/Analysis/inlining/false-positive-suppression.m +++ b/test/Analysis/inlining/false-positive-suppression.m @@ -45,6 +45,8 @@ -(int *)methodReturningNull; @property(readonly) int *propertyReturningNull; +@property(readonly) int *synthesizedProperty; + @end @implementation SomeClass @@ -72,3 +74,14 @@ void testPropertyReturningNull(SomeClass *sc) { // expected-warning@-2 {{Dereference of null pointer}} #endif } + +void testSynthesizedPropertyReturningNull(SomeClass *sc) { + if (sc.synthesizedProperty) + return; + + int *result = sc.synthesizedProperty; + *result = 1; +#ifndef SUPPRESSED + // expected-warning@-2 {{Dereference of null pointer}} +#endif +} From 530f6544f8a244b6f516a7605afd571845f5e6c9 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Tue, 12 Apr 2016 23:10:58 +0000 Subject: [PATCH 475/742] [ObjC] Pop all cleanups created in EmitObjCForCollectionStmt before exiting the for-in loop. This commit fixes a bug where EmitObjCForCollectionStmt didn't pop cleanups for captures. For example, in the following for-in loop, a block which captures self is passed to foo1: for (id x in [self foo1:^{ use(self); }]) { use(x); break; } Previously, the code in EmitObjCForCollectionStmt wouldn't pop the cleanup for the captured self before exiting the loop, which caused code-gen to generate an IR in which objc_release was called twice on the captured self. This commit fixes the bug by entering a RunCleanupsScope before the loop condition is evaluated and forcing its cleanup before exiting the loop. rdar://problem/16865751 Differential Revision: http://reviews.llvm.org/D18618 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@266147 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 179aac0a8fa1e1e33f00b16ee85a871929915db4) --- lib/CodeGen/CGObjC.cpp | 7 ++--- test/CodeGenObjC/arc-foreach.m | 51 ++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/lib/CodeGen/CGObjC.cpp b/lib/CodeGen/CGObjC.cpp index 1c165594612..182dacfb620 100644 --- a/lib/CodeGen/CGObjC.cpp +++ b/lib/CodeGen/CGObjC.cpp @@ -1485,6 +1485,8 @@ void CodeGenFunction::EmitObjCForCollectionStmt(const ObjCForCollectionStmt &S){ ArrayType::Normal, 0); Address ItemsPtr = CreateMemTemp(ItemsTy, "items.ptr"); + RunCleanupsScope ForScope(*this); + // Emit the collection pointer. In ARC, we do a retain. llvm::Value *Collection; if (getLangOpts().ObjCAutoRefCount) { @@ -1725,10 +1727,7 @@ void CodeGenFunction::EmitObjCForCollectionStmt(const ObjCForCollectionStmt &S){ if (DI) DI->EmitLexicalBlockEnd(Builder, S.getSourceRange().getEnd()); - // Leave the cleanup we entered in ARC. - if (getLangOpts().ObjCAutoRefCount) - PopCleanupBlock(); - + ForScope.ForceCleanup(); EmitBlock(LoopEnd.getBlock()); } diff --git a/test/CodeGenObjC/arc-foreach.m b/test/CodeGenObjC/arc-foreach.m index 90d9c1f1261..db150e88a59 100644 --- a/test/CodeGenObjC/arc-foreach.m +++ b/test/CodeGenObjC/arc-foreach.m @@ -170,4 +170,55 @@ void test3(NSArray *array) { // CHECK-LP64-NEXT: br label [[L]] } +@interface NSObject @end + +@interface I1 : NSObject +- (NSArray *) foo1:(void (^)(void))block; +- (void) foo2; +@end + +NSArray *array4; + +@implementation I1 : NSObject +- (NSArray *) foo1:(void (^)(void))block { + block(); + return array4; +} + +- (void) foo2 { + for (id x in [self foo1:^{ use(self); }]) { + use(x); + break; + } +} +@end + +// CHECK-LP64-LABEL: define internal void @"\01-[I1 foo2]"( +// CHECK-LP64: [[SELF_ADDR:%.*]] = alloca [[TY:%.*]]*, +// CHECK-LP64: [[BLOCK:%.*]] = alloca <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, [[TY]]* }>, +// CHECK-LP64: store [[TY]]* %self, [[TY]]** [[SELF_ADDR]] +// CHECK-LP64: [[T0:%.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, [[TY]]* }>, <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, [[TY]]* }>* [[BLOCK]], i32 0, i32 5 +// CHECK-LP64: [[BC:%.*]] = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, [[TY]]* }>, <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, [[TY]]* }>* [[BLOCK]], i32 0, i32 5 +// CHECK-LP64: [[T1:%.*]] = load [[TY]]*, [[TY]]** [[SELF_ADDR]] +// CHECK-LP64: [[T2:%.*]] = bitcast [[TY]]* [[T1]] to i8* +// CHECK-LP64: [[T3:%.*]] = call i8* @objc_retain(i8* [[T2]]) +// CHECK-LP64: [[T4:%.*]] = bitcast i8* [[T3]] to [[TY]]* +// CHECK-LP64: store [[TY]]* [[T4]], [[TY]]** [[BC]] + +// CHECK-LP64: [[T5:%.*]] = bitcast [[TY]]** [[T0]] to i8** +// CHECK-LP64: call void @objc_storeStrong(i8** [[T5]], i8* null) +// CHECK-LP64: switch i32 {{%.*}}, label %[[UNREACHABLE:.*]] [ +// CHECK-LP64-NEXT: i32 0, label %[[CLEANUP_CONT:.*]] +// CHECK-LP64-NEXT: i32 2, label %[[FORCOLL_END:.*]] +// CHECK-LP64-NEXT: ] + +// CHECK-LP64: {{^|:}}[[CLEANUP_CONT]] +// CHECK-LP64-NEXT: br label %[[FORCOLL_END]] + +// CHECK-LP64: {{^|:}}[[FORCOLL_END]] +// CHECK-LP64-NEXT: ret void + +// CHECK-LP64: {{^|:}}[[UNREACHABLE]] +// CHECK-LP64-NEXT: unreachable + // CHECK-LP64: attributes [[NUW]] = { nounwind } From e425c16980801ba154ce0eca06c3b1e2a8e9cf64 Mon Sep 17 00:00:00 2001 From: Tim Northover Date: Wed, 13 Apr 2016 17:08:55 +0000 Subject: [PATCH 476/742] AArch64: allow 64-bit access to sysregs. Although all the registers are actually 32-bits, I think we have to assume the high 32-bits could be RES0 (the ARM ARM is unclear). If so, reading as a 32-bit register can require extra zero extension operations. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@266212 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/SemaChecking.cpp | 2 +- test/Sema/aarch64-special-register.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index e56483133df..12777be9404 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -933,7 +933,7 @@ bool Sema::CheckAArch64BuiltinFunctionCall(unsigned BuiltinID, if (BuiltinID == AArch64::BI__builtin_arm_rsr64 || BuiltinID == AArch64::BI__builtin_arm_wsr64) - return SemaBuiltinARMSpecialReg(BuiltinID, TheCall, 0, 5, false); + return SemaBuiltinARMSpecialReg(BuiltinID, TheCall, 0, 5, true); if (BuiltinID == AArch64::BI__builtin_arm_rsr || BuiltinID == AArch64::BI__builtin_arm_rsrp || diff --git a/test/Sema/aarch64-special-register.c b/test/Sema/aarch64-special-register.c index 40d4033967f..a4fb92b5235 100644 --- a/test/Sema/aarch64-special-register.c +++ b/test/Sema/aarch64-special-register.c @@ -13,7 +13,7 @@ void wsrp_1(void *v) { } void wsr64_1(unsigned long v) { - __builtin_arm_wsr64("sysreg", v); //expected-error {{invalid special register for builtin}} + __builtin_arm_wsr64("sysreg", v); } unsigned rsr_1() { @@ -25,7 +25,7 @@ void *rsrp_1() { } unsigned long rsr64_1() { - return __builtin_arm_rsr64("sysreg"); //expected-error {{invalid special register for builtin}} + return __builtin_arm_rsr64("sysreg"); } void wsr_2(unsigned v) { From 2ae82e2f6b23a2493d7d39d5eee66e5f7e1f028b Mon Sep 17 00:00:00 2001 From: Tim Northover Date: Wed, 13 Apr 2016 17:08:51 +0000 Subject: [PATCH 477/742] ARM: make Darwin's "-arch armv7em" default to hard-float. We've already paid the price for separate "armv7m" and "armv7em" slices (support in other tools), it's silly to make them identical other than the default CPU. rdar://23055688 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@266211 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Driver/Tools.cpp | 7 ++++++- test/Driver/darwin-embedded.c | 15 ++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index e644742188c..6b77f82f652 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -738,7 +738,12 @@ arm::FloatABI arm::getARMFloatABI(const ToolChain &TC, const ArgList &Args) { break; default: // Assume "soft", but warn the user we are guessing. - ABI = FloatABI::Soft; + if (Triple.isOSBinFormatMachO() && + Triple.getSubArch() == llvm::Triple::ARMSubArch_v7em) + ABI = FloatABI::Hard; + else + ABI = FloatABI::Soft; + if (Triple.getOS() != llvm::Triple::UnknownOS || !Triple.isOSBinFormatMachO()) D.Diag(diag::warn_drv_assuming_mfloat_abi_is) << "soft"; diff --git a/test/Driver/darwin-embedded.c b/test/Driver/darwin-embedded.c index 66b7bd9fa7f..beb8b195c42 100644 --- a/test/Driver/darwin-embedded.c +++ b/test/Driver/darwin-embedded.c @@ -1,6 +1,6 @@ // RUN: %clang -target x86_64-apple-darwin -arch armv6m -resource-dir=%S/Inputs/resource_dir %s -### 2> %t // RUN: %clang -target x86_64-apple-darwin -arch armv7em -resource-dir=%S/Inputs/resource_dir %s -### 2>> %t -// RUN: %clang -target x86_64-apple-darwin -arch armv7em -mhard-float -resource-dir=%S/Inputs/resource_dir %s -### 2>> %t +// RUN: %clang -target x86_64-apple-darwin -arch armv7em -mfloat-abi=soft -resource-dir=%S/Inputs/resource_dir %s -### 2>> %t // RUN: %clang -target x86_64-apple-darwin -arch armv7m -fPIC -resource-dir=%S/Inputs/resource_dir %s -### 2>> %t // RUN: %clang -target x86_64-apple-darwin -arch armv7em -fPIC -mfloat-abi=hard -resource-dir=%S/Inputs/resource_dir %s -### 2>> %t @@ -17,17 +17,18 @@ // CHECK: "-mfloat-abi" "soft" // CHECK: libclang_rt.soft_static.a -// ARMv7em does, but defaults to soft +// ARMv7em does // CHECK-LABEL: Target: // CHECK-NOT: warning: unknown platform -// CHECK: "-mfloat-abi" "soft" -// CHECK: libclang_rt.soft_static.a +// CHECK: "-mfloat-abi" "hard" +// CHECK: libclang_rt.hard_static.a -// Which can be overridden +// but the ABI can be overridden // CHECK-LABEL: Target: // CHECK-NOT: warning: unknown platform -// CHECK: "-mfloat-abi" "hard" -// CHECK: libclang_rt.hard_static.a +// CHECK: "-target-feature" "+soft-float" +// CHECK: "-mfloat-abi" "soft" +// CHECK: libclang_rt.soft_static.a // ARMv7m has no float either // CHECK-LABEL: Target: From 79d8cfeffc76def37e268153a8dadf09e33801de Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Tue, 12 Apr 2016 23:01:55 +0000 Subject: [PATCH 478/742] ObjC class properties: add diagnostics for unimplemented class properties. rdar://24711047 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@266146 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 10 ++ lib/Sema/SemaObjCProperty.cpp | 105 ++++++++++++--------- test/Parser/objc-class-property.m | 1 - test/SemaObjC/objc-class-property.m | 6 +- 4 files changed, 76 insertions(+), 46 deletions(-) diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index e8cbf79e1bd..d24ef1a9add 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -5628,6 +5628,16 @@ def note_parameter_here : Note< def note_method_return_type_change : Note< "compiler has implicitly changed method %0 return type">; +def warn_impl_required_for_class_property : Warning< + "class property %0 requires method %1 to be defined - " + "use @dynamic or provide a method implementation " + "in this class implementation">, + InGroup; +def warn_impl_required_in_category_for_class_property : Warning< + "class property %0 requires method %1 to be defined - " + "use @dynamic or provide a method implementation in this category">, + InGroup; + // C++ casts // These messages adhere to the TryCast pattern: %0 is an int specifying the // cast type, %1 is the source type, %2 is the destination type. diff --git a/lib/Sema/SemaObjCProperty.cpp b/lib/Sema/SemaObjCProperty.cpp index ef7fc6c2089..94f03280963 100644 --- a/lib/Sema/SemaObjCProperty.cpp +++ b/lib/Sema/SemaObjCProperty.cpp @@ -1532,38 +1532,50 @@ bool Sema::DiagnosePropertyAccessorMismatch(ObjCPropertyDecl *property, /// CollectImmediateProperties - This routine collects all properties in /// the class and its conforming protocols; but not those in its super class. -static void CollectImmediateProperties(ObjCContainerDecl *CDecl, - ObjCContainerDecl::PropertyMap &PropMap, - ObjCContainerDecl::PropertyMap &SuperPropMap, - bool IncludeProtocols = true) { - +static void +CollectImmediateProperties(ObjCContainerDecl *CDecl, + ObjCContainerDecl::PropertyMap &PropMap, + ObjCContainerDecl::PropertyMap &SuperPropMap, + bool CollectClassPropsOnly = false, + bool IncludeProtocols = true) { if (ObjCInterfaceDecl *IDecl = dyn_cast(CDecl)) { - for (auto *Prop : IDecl->properties()) + for (auto *Prop : IDecl->properties()) { + if (CollectClassPropsOnly && !Prop->isClassProperty()) + continue; PropMap[std::make_pair(Prop->getIdentifier(), Prop->isClassProperty())] = Prop; + } // Collect the properties from visible extensions. for (auto *Ext : IDecl->visible_extensions()) - CollectImmediateProperties(Ext, PropMap, SuperPropMap, IncludeProtocols); + CollectImmediateProperties(Ext, PropMap, SuperPropMap, + CollectClassPropsOnly, IncludeProtocols); if (IncludeProtocols) { // Scan through class's protocols. for (auto *PI : IDecl->all_referenced_protocols()) - CollectImmediateProperties(PI, PropMap, SuperPropMap); + CollectImmediateProperties(PI, PropMap, SuperPropMap, + CollectClassPropsOnly); } } if (ObjCCategoryDecl *CATDecl = dyn_cast(CDecl)) { - for (auto *Prop : CATDecl->properties()) + for (auto *Prop : CATDecl->properties()) { + if (CollectClassPropsOnly && !Prop->isClassProperty()) + continue; PropMap[std::make_pair(Prop->getIdentifier(), Prop->isClassProperty())] = Prop; + } if (IncludeProtocols) { // Scan through class's protocols. for (auto *PI : CATDecl->protocols()) - CollectImmediateProperties(PI, PropMap, SuperPropMap); + CollectImmediateProperties(PI, PropMap, SuperPropMap, + CollectClassPropsOnly); } } else if (ObjCProtocolDecl *PDecl = dyn_cast(CDecl)) { for (auto *Prop : PDecl->properties()) { + if (CollectClassPropsOnly && !Prop->isClassProperty()) + continue; ObjCPropertyDecl *PropertyFromSuper = SuperPropMap[std::make_pair(Prop->getIdentifier(), Prop->isClassProperty())]; @@ -1578,9 +1590,10 @@ static void CollectImmediateProperties(ObjCContainerDecl *CDecl, PropEntry = Prop; } } - // scan through protocol's protocols. + // Scan through protocol's protocols. for (auto *PI : PDecl->protocols()) - CollectImmediateProperties(PI, PropMap, SuperPropMap); + CollectImmediateProperties(PI, PropMap, SuperPropMap, + CollectClassPropsOnly); } } @@ -1769,19 +1782,22 @@ static void DiagnoseUnimplementedAccessor(Sema &S, (PrimaryClass == nullptr || !PrimaryClass->lookupPropertyAccessor(Method, C, Prop->isClassProperty()))) { - S.Diag(IMPDecl->getLocation(), - isa(CDecl) ? - diag::warn_setter_getter_impl_required_in_category : - diag::warn_setter_getter_impl_required) - << Prop->getDeclName() << Method; - S.Diag(Prop->getLocation(), - diag::note_property_declare); - if (S.LangOpts.ObjCDefaultSynthProperties && - S.LangOpts.ObjCRuntime.isNonFragile()) - if (ObjCInterfaceDecl *ID = dyn_cast(CDecl)) - if (const ObjCInterfaceDecl *RID = ID->isObjCRequiresPropertyDefs()) - S.Diag(RID->getLocation(), diag::note_suppressed_class_declare); - } + unsigned diag = + isa(CDecl) + ? (Prop->isClassProperty() + ? diag::warn_impl_required_in_category_for_class_property + : diag::warn_setter_getter_impl_required_in_category) + : (Prop->isClassProperty() + ? diag::warn_impl_required_for_class_property + : diag::warn_setter_getter_impl_required); + S.Diag(IMPDecl->getLocation(), diag) << Prop->getDeclName() << Method; + S.Diag(Prop->getLocation(), diag::note_property_declare); + if (S.LangOpts.ObjCDefaultSynthProperties && + S.LangOpts.ObjCRuntime.isNonFragile()) + if (ObjCInterfaceDecl *ID = dyn_cast(CDecl)) + if (const ObjCInterfaceDecl *RID = ID->isObjCRequiresPropertyDefs()) + S.Diag(RID->getLocation(), diag::note_suppressed_class_declare); + } } void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl, @@ -1790,25 +1806,27 @@ void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl, ObjCContainerDecl::PropertyMap PropMap; ObjCInterfaceDecl *IDecl = dyn_cast(CDecl); - if (!SynthesizeProperties) { - ObjCContainerDecl::PropertyMap NoNeedToImplPropMap; - // Gather properties which need not be implemented in this class - // or category. - if (!IDecl) - if (ObjCCategoryDecl *C = dyn_cast(CDecl)) { - // For categories, no need to implement properties declared in - // its primary class (and its super classes) if property is - // declared in one of those containers. - if ((IDecl = C->getClassInterface())) { - ObjCInterfaceDecl::PropertyDeclOrder PO; - IDecl->collectPropertiesToImplement(NoNeedToImplPropMap, PO); - } + // Since we don't synthesize class properties, we should emit diagnose even + // if SynthesizeProperties is true. + ObjCContainerDecl::PropertyMap NoNeedToImplPropMap; + // Gather properties which need not be implemented in this class + // or category. + if (!IDecl) + if (ObjCCategoryDecl *C = dyn_cast(CDecl)) { + // For categories, no need to implement properties declared in + // its primary class (and its super classes) if property is + // declared in one of those containers. + if ((IDecl = C->getClassInterface())) { + ObjCInterfaceDecl::PropertyDeclOrder PO; + IDecl->collectPropertiesToImplement(NoNeedToImplPropMap, PO); } - if (IDecl) - CollectSuperClassPropertyImplementations(IDecl, NoNeedToImplPropMap); + } + if (IDecl) + CollectSuperClassPropertyImplementations(IDecl, NoNeedToImplPropMap); - CollectImmediateProperties(CDecl, PropMap, NoNeedToImplPropMap); - } + // When SynthesizeProperties is true, we only check class properties. + CollectImmediateProperties(CDecl, PropMap, NoNeedToImplPropMap, + SynthesizeProperties/*CollectClassPropsOnly*/); // Scan the @interface to see if any of the protocols it adopts // require an explicit implementation, via attribute @@ -1830,6 +1848,7 @@ void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl, ObjCContainerDecl::PropertyMap NoNeedToImplPropMap; LazyMap.reset(new ObjCContainerDecl::PropertyMap()); CollectImmediateProperties(CDecl, *LazyMap, NoNeedToImplPropMap, + /* CollectClassPropsOnly */ false, /* IncludeProtocols */ false); } // Add the properties of 'PDecl' to the list of properties that @@ -1872,7 +1891,7 @@ void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl, for (ObjCContainerDecl::PropertyMap::iterator P = PropMap.begin(), E = PropMap.end(); P != E; ++P) { ObjCPropertyDecl *Prop = P->second; - // Is there a matching propery synthesize/dynamic? + // Is there a matching property synthesize/dynamic? if (Prop->isInvalidDecl() || Prop->getPropertyImplementation() == ObjCPropertyDecl::Optional || PropImplMap.count(Prop) || diff --git a/test/Parser/objc-class-property.m b/test/Parser/objc-class-property.m index 202352c33b7..e4c3b0766b4 100644 --- a/test/Parser/objc-class-property.m +++ b/test/Parser/objc-class-property.m @@ -15,7 +15,6 @@ @interface A : Root { @property int z; @property(readonly) int ro, ro2; @property (class) int c; -@property (class) int c2; @end @implementation A diff --git a/test/SemaObjC/objc-class-property.m b/test/SemaObjC/objc-class-property.m index 37a8178ceb6..5e1b866cbc8 100644 --- a/test/SemaObjC/objc-class-property.m +++ b/test/SemaObjC/objc-class-property.m @@ -18,11 +18,13 @@ @interface A : Root { @property int z; @property(readonly) int ro, ro2; @property (class) int c; -@property (class) int c2; +@property (class) int c2; // expected-note {{property declared here}} \ + // expected-note {{property declared here}} @property (class) int x; @end -@implementation A +@implementation A // expected-warning {{class property 'c2' requires method 'c2' to be defined}} \ + // expected-warning {{class property 'c2' requires method 'setC2:' to be defined}} @dynamic x; // refers to the instance property @dynamic (class) x; // refers to the class property @synthesize z, c2; // expected-error {{@synthesize not allowed on a class property 'c2'}} From f447afcb3a3af1d425a5f39323462235b6fc5c8e Mon Sep 17 00:00:00 2001 From: Teresa Johnson Date: Wed, 30 Mar 2016 13:59:49 +0000 Subject: [PATCH 479/742] Prepare tests for change to emit Module SourceFileName to LLVM assembly Modify these tests to ignore the source file name when looking for the expected string. It was already catching the source file name once via the ModuleID, and will catch it another time with an impending change to LLVM to serialize out the module's SourceFileName. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@264868 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/CodeGen/sret.c | 2 +- test/CodeGen/sret2.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/CodeGen/sret.c b/test/CodeGen/sret.c index 5f0d07404a1..201e3b87f8b 100644 --- a/test/CodeGen/sret.c +++ b/test/CodeGen/sret.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 %s -emit-llvm -o - | grep sret | count 5 +// RUN: %clang_cc1 %s -emit-llvm -o - | grep sret | grep -v 'sret.c' | count 4 struct abc { long a; diff --git a/test/CodeGen/sret2.c b/test/CodeGen/sret2.c index d103d87610f..0e254ed914a 100644 --- a/test/CodeGen/sret2.c +++ b/test/CodeGen/sret2.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 %s -emit-llvm -o - | grep sret | count 2 +// RUN: %clang_cc1 %s -emit-llvm -o - | grep sret | grep -v 'sret2.c' | count 1 struct abc { long a; From 3a6eb153c4d4bc3646e8e8879be8e6d7bc92d451 Mon Sep 17 00:00:00 2001 From: Mehdi Amini Date: Mon, 11 Apr 2016 18:45:20 +0000 Subject: [PATCH 480/742] Emit the module hash by default with -flto=thin. Reviewers: tejohnson Subscribers: joker.eph, cfe-commits Differential Revision: http://reviews.llvm.org/D18947 From: Mehdi Amini git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265977 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/BackendUtil.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/CodeGen/BackendUtil.cpp b/lib/CodeGen/BackendUtil.cpp index 919bee94ee3..8f4a81216f1 100644 --- a/lib/CodeGen/BackendUtil.cpp +++ b/lib/CodeGen/BackendUtil.cpp @@ -670,7 +670,8 @@ void EmitAssemblyHelper::EmitAssembly(BackendAction Action, case Backend_EmitBC: getPerModulePasses()->add(createBitcodeWriterPass( - *OS, CodeGenOpts.EmitLLVMUseLists, CodeGenOpts.EmitSummaryIndex)); + *OS, CodeGenOpts.EmitLLVMUseLists, CodeGenOpts.EmitSummaryIndex, + CodeGenOpts.EmitSummaryIndex)); break; case Backend_EmitLL: From 6d5789da586cf569360bd36902d1b7c6672334e1 Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes Date: Wed, 13 Apr 2016 20:59:07 +0000 Subject: [PATCH 481/742] [SemaObjC] Properly handle mix between type arguments and protocols. Under certain conditions clang currently fails to properly diagnostic ObjectC parameter list when type args and protocols are mixed in the same list. This happens when the first item in the parameter list is a (1) protocol, (2) unknown type or (3) a list of protocols/unknown types up to the first type argument. Fix the problem to report the proper error, example: NSArray>> *foo = @[@"a"]; NSNumber *bar = foo[0]; NSLog(@"%@", bar); $ clang ... x.m:7:13: error: angle brackets contain both a type ('NSValue') and a protocol ('M') NSArray>> *foo = @[@"a"]; ~ ^ Differential Revision: http://reviews.llvm.org/D18997 rdar://problem/22204367 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@266245 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 55cf4bd859f8f8fdc8f1b7bbd07e7d3c12c284a2) --- include/clang/Sema/Sema.h | 6 ++++ lib/Parse/ParseObjc.cpp | 47 ++++++++++++++++++++++++--- lib/Sema/SemaDeclObjC.cpp | 18 +++++++--- test/SemaObjC/parameterized_classes.m | 4 +++ 4 files changed, 66 insertions(+), 9 deletions(-) diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 237efafa21a..0c03b899633 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -7306,6 +7306,12 @@ class Sema { ArrayRef ProtocolId, SmallVectorImpl &Protocols); + void DiagnoseTypeArgsAndProtocols(IdentifierInfo *ProtocolId, + SourceLocation ProtocolLoc, + IdentifierInfo *TypeArgId, + SourceLocation TypeArgLoc, + bool SelectProtocolFirst = false); + /// Given a list of identifiers (and their locations), resolve the /// names to either Objective-C protocol qualifiers or type /// arguments, as appropriate. diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index 4ae2afe068f..e47e361c32b 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/lib/Parse/ParseObjc.cpp @@ -1690,11 +1690,18 @@ void Parser::parseObjCTypeArgsOrProtocolQualifiers( return; } - // We syntactically matched a type argument, so commit to parsing - // type arguments. + // We parsed an identifier list but stumbled into non single identifiers, this + // means we might (a) check that what we already parsed is a legitimate type + // (not a protocol or unknown type) and (b) parse the remaining ones, which + // must all be type args. // Convert the identifiers into type arguments. bool invalid = false; + IdentifierInfo *foundProtocolId = nullptr, *foundValidTypeId = nullptr; + SourceLocation foundProtocolSrcLoc, foundValidTypeSrcLoc; + SmallVector unknownTypeArgs; + SmallVector unknownTypeArgsLoc; + for (unsigned i = 0, n = identifiers.size(); i != n; ++i) { ParsedType typeArg = Actions.getTypeName(*identifiers[i], identifierLocs[i], getCurScope()); @@ -1708,17 +1715,32 @@ void Parser::parseObjCTypeArgsOrProtocolQualifiers( // Form a declarator to turn this into a type. Declarator D(DS, Declarator::TypeNameContext); TypeResult fullTypeArg = Actions.ActOnTypeName(getCurScope(), D); - if (fullTypeArg.isUsable()) + if (fullTypeArg.isUsable()) { typeArgs.push_back(fullTypeArg.get()); - else + if (!foundValidTypeId) { + foundValidTypeId = identifiers[i]; + foundValidTypeSrcLoc = identifierLocs[i]; + } + } else { invalid = true; + unknownTypeArgs.push_back(identifiers[i]); + unknownTypeArgsLoc.push_back(identifierLocs[i]); + } } else { invalid = true; + if (!Actions.LookupProtocol(identifiers[i], identifierLocs[i])) { + unknownTypeArgs.push_back(identifiers[i]); + unknownTypeArgsLoc.push_back(identifierLocs[i]); + } else if (!foundProtocolId) { + foundProtocolId = identifiers[i]; + foundProtocolSrcLoc = identifierLocs[i]; + } } } // Continue parsing type-names. do { + Token CurTypeTok = Tok; TypeResult typeArg = ParseTypeName(); // Consume the '...' for a pack expansion. @@ -1730,11 +1752,28 @@ void Parser::parseObjCTypeArgsOrProtocolQualifiers( if (typeArg.isUsable()) { typeArgs.push_back(typeArg.get()); + if (!foundValidTypeId) { + foundValidTypeId = CurTypeTok.getIdentifierInfo(); + foundValidTypeSrcLoc = CurTypeTok.getLocation(); + } } else { invalid = true; } } while (TryConsumeToken(tok::comma)); + // Diagnose the mix between type args and protocols. + if (foundProtocolId && foundValidTypeId) + Actions.DiagnoseTypeArgsAndProtocols(foundProtocolId, foundProtocolSrcLoc, + foundValidTypeId, + foundValidTypeSrcLoc); + + // Diagnose unknown arg types. + ParsedType T; + if (unknownTypeArgs.size()) + for (unsigned i = 0, e = unknownTypeArgsLoc.size(); i < e; ++i) + Actions.DiagnoseUnknownTypeName(unknownTypeArgs[i], unknownTypeArgsLoc[i], + getCurScope(), nullptr, T); + // Parse the closing '>'. SourceLocation rAngleLoc; (void)ParseGreaterThanInTemplateList(rAngleLoc, consumeLastToken, diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index ba5edcf8977..10638ff44b6 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -1306,6 +1306,16 @@ class ObjCTypeArgOrProtocolValidatorCCC : public CorrectionCandidateCallback { }; } // end anonymous namespace +void Sema::DiagnoseTypeArgsAndProtocols(IdentifierInfo *ProtocolId, + SourceLocation ProtocolLoc, + IdentifierInfo *TypeArgId, + SourceLocation TypeArgLoc, + bool SelectProtocolFirst) { + Diag(TypeArgLoc, diag::err_objc_type_args_and_protocols) + << SelectProtocolFirst << TypeArgId << ProtocolId + << SourceRange(ProtocolLoc); +} + void Sema::actOnObjCTypeArgsOrProtocolQualifiers( Scope *S, ParsedType baseType, @@ -1573,11 +1583,9 @@ void Sema::actOnObjCTypeArgsOrProtocolQualifiers( // We have a conflict: some names refer to protocols and others // refer to types. - Diag(identifierLocs[i], diag::err_objc_type_args_and_protocols) - << (protocols[i] != nullptr) - << identifiers[i] - << identifiers[0] - << SourceRange(identifierLocs[0]); + DiagnoseTypeArgsAndProtocols(identifiers[0], identifierLocs[0], + identifiers[i], identifierLocs[i], + protocols[i] != nullptr); protocols.clear(); typeArgs.clear(); diff --git a/test/SemaObjC/parameterized_classes.m b/test/SemaObjC/parameterized_classes.m index 7f380a10547..1004fd3c4b1 100644 --- a/test/SemaObjC/parameterized_classes.m +++ b/test/SemaObjC/parameterized_classes.m @@ -240,6 +240,10 @@ - (X)extMethod:(Y)param; // Type/protocol conflict. typedef PC4 typeArgsProtocolQualsConflict1; // expected-error{{angle brackets contain both a type ('ObjCStringRef') and a protocol ('NSCopying')}} +typedef PC4 typeArgsProtocolQualsConflict2; // expected-error{{angle brackets contain both a type ('NSString') and a protocol ('NSCopying')}} +typedef PC4 typeArgsProtocolQualsConflict3; // expected-error{{angle brackets contain both a type ('NSString') and a protocol ('NSCopying')}} expected-error{{unknown type name 'UnknownType'}} +typedef PC4 typeArgsProtocolQualsConflict4; // expected-error{{unknown type name 'UnknownType'}} +typedef PC4 typeArgsProtocolQualsConflict5; // expected-error{{angle brackets contain both a type ('NSString') and a protocol ('NSCopying')}} // Handling the '>>' in type argument lists. typedef PC4, NSObject *, id> typeArgs6; From 19c7d601563a4c42276494c350e9c0b1446af425 Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Wed, 13 Apr 2016 23:43:56 +0000 Subject: [PATCH 482/742] ObjC kindof: order the methods in global pool relative to availability. r265877 tries to put methods that are deprecated or unavailable to the front of the global pool to emit diagnostics, but it breaks some of our existing codes that depend on choosing a certain method for id lookup. This commit orders the methods with the same declaration with respect to the availability, but do not order methods with different declaration. rdar://25707511 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@266264 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/SemaDeclObjC.cpp | 42 +++++++++++++++++++-------- test/SemaObjC/kindof.m | 13 +++++++++ test/SemaObjC/multiple-method-names.m | 4 +-- 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index 10638ff44b6..17390be33f5 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -3219,22 +3219,43 @@ void Sema::addMethodToGlobalList(ObjCMethodList *List, // We've seen a method with this name, see if we have already seen this type // signature. - ObjCMethodList *Head = List; ObjCMethodList *Previous = List; + ObjCMethodList *ListWithSameDeclaration = nullptr; for (; List; Previous = List, List = List->getNext()) { // If we are building a module, keep all of the methods. if (getLangOpts().CompilingModule) continue; + bool SameDeclaration = MatchTwoMethodDeclarations(Method, + List->getMethod()); // Looking for method with a type bound requires the correct context exists. - // We need to insert this method into the list if the context is different. - if (!MatchTwoMethodDeclarations(Method, List->getMethod()) || + // We need to insert a method into the list if the context is different. + // If the method's declaration matches the list + // a> the method belongs to a different context: we need to insert it, in + // order to emit the availability message, we need to prioritize over + // availability among the methods with the same declaration. + // b> the method belongs to the same context: there is no need to insert a + // new entry. + // If the method's declaration does not match the list, we insert it to the + // end. + if (!SameDeclaration || !isMethodContextSameForKindofLookup(Method, List->getMethod())) { // Even if two method types do not match, we would like to say // there is more than one declaration so unavailability/deprecated // warning is not too noisy. if (!Method->isDefined()) List->setHasMoreThanOneDecl(true); + + // For methods with the same declaration, the one that is deprecated + // should be put in the front for better diagnostics. + if (Method->isDeprecated() && SameDeclaration && + !ListWithSameDeclaration && !List->getMethod()->isDeprecated()) + ListWithSameDeclaration = List; + + if (Method->isUnavailable() && SameDeclaration && + !ListWithSameDeclaration && + List->getMethod()->getAvailability() < AR_Deprecated) + ListWithSameDeclaration = List; continue; } @@ -3271,15 +3292,12 @@ void Sema::addMethodToGlobalList(ObjCMethodList *List, // This is extremely rare. Only 1% of Cocoa selectors are "overloaded". ObjCMethodList *Mem = BumpAlloc.Allocate(); - // We tried to prioritize the list by putting deprecated and unavailable - // methods in the front. - if ((Method->isDeprecated() && !Head->getMethod()->isDeprecated()) || - (Method->isUnavailable() && - Head->getMethod()->getAvailability() < AR_Deprecated)) { - auto *List = new (Mem) ObjCMethodList(*Head); - // FIXME: should we clear the other bits in Head? - Head->setMethod(Method); - Head->setNext(List); + // We insert it right before ListWithSameDeclaration. + if (ListWithSameDeclaration) { + auto *List = new (Mem) ObjCMethodList(*ListWithSameDeclaration); + // FIXME: should we clear the other bits in ListWithSameDeclaration? + ListWithSameDeclaration->setMethod(Method); + ListWithSameDeclaration->setNext(List); return; } diff --git a/test/SemaObjC/kindof.m b/test/SemaObjC/kindof.m index 95c7f993d17..919ca960a9d 100644 --- a/test/SemaObjC/kindof.m +++ b/test/SemaObjC/kindof.m @@ -25,6 +25,7 @@ - (NSObject *)retain; @end @interface NSString : NSObject // expected-note{{receiver is instance of class declared here}} +- (void)compare:(NSString *)string; - (NSString *)stringByAppendingString:(NSString *)string; + (instancetype)string; @end @@ -289,6 +290,18 @@ void foo() { } } +typedef const struct CGPath *CGPathRef; +@interface C : NSObject +@property (copy) NSString *path; +@end +@interface D : NSObject +@property CGPathRef path __attribute__((availability(macosx,unavailable))); +@end +// Make sure we choose "NSString *path" for [s1 path]. +void bar(id s1, id s2) { + return [[s1 path] compare:[s2 path]]; +} + // --------------------------------------------------------------------------- // __kindof within specialized types // --------------------------------------------------------------------------- diff --git a/test/SemaObjC/multiple-method-names.m b/test/SemaObjC/multiple-method-names.m index a8707904b56..9fd83b208ab 100644 --- a/test/SemaObjC/multiple-method-names.m +++ b/test/SemaObjC/multiple-method-names.m @@ -2,11 +2,11 @@ // PR22047 @interface Face0 -- (void)foo:(float)i; // expected-note {{also found}} +- (void)foo:(float)i; // expected-note {{using}} @end @interface Face1 -- (void)foo:(int)i __attribute__((unavailable)); // expected-note {{using}} +- (void)foo:(int)i __attribute__((unavailable)); @end @interface Face2 From 5672f681ae91fa5f853bb7bfcab3818589529e0d Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes Date: Thu, 17 Mar 2016 02:20:43 +0000 Subject: [PATCH 483/742] [VFS] Add support for handling path traversals This was applied twice r261551 and 263617 and later reverted because: (1) Windows bot failing on unittests. Change the current behavior to do not handle path traversals on windows. (2) Windows bot failed to include llvm/Config/config.h in order to use HAVE_REALPATH. Use LLVM_ON_UNIX instead, as done in lib/Basic/FileManager.cpp. Handle ".", ".." and "./" with trailing slashes while collecting files to be dumped into the vfs overlay directory. Include the support for symlinks into components. Given the path: /install-dir/bin/../lib/clang/3.8.0/include/altivec.h, if "bin" component is a symlink, it's not safe to use `path::remove_dots` here, and `realpath` is used to get the right answer. Since `realpath` is expensive, we only do it at collecting time (which only happens during the crash reproducer) and cache the base directory for fast lookups. Overall, this makes the input to the VFS YAML file to be canonicalized to never contain traversal components. Differential Revision: http://reviews.llvm.org/D17104 rdar://problem/24499339 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263686 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit ccefdc8cb94ecb4121695b30c2e2d0054d6b7ded) git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263691 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 61f212aa6dd46c684d30e06c2d6e4ef53ebc225a) git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263718 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit c79911c0d2409744f50b11567a938b43473a486d) --- lib/Basic/VirtualFileSystem.cpp | 80 +++++++++++++++---- lib/Frontend/ModuleDependencyCollector.cpp | 80 +++++++++++++++++-- .../crash-recovery/usr/include/module.map | 6 ++ .../Inputs/crash-recovery/usr/include/stdio.h | 3 + .../usr/include/tcl-private/header.h | 2 + .../crash-vfs-path-symlink-component.m | 72 +++++++++++++++++ test/Modules/crash-vfs-path-traversal.m | 60 ++++++++++++++ 7 files changed, 279 insertions(+), 24 deletions(-) create mode 100644 test/Modules/Inputs/crash-recovery/usr/include/module.map create mode 100644 test/Modules/Inputs/crash-recovery/usr/include/stdio.h create mode 100644 test/Modules/Inputs/crash-recovery/usr/include/tcl-private/header.h create mode 100644 test/Modules/crash-vfs-path-symlink-component.m create mode 100644 test/Modules/crash-vfs-path-traversal.m diff --git a/lib/Basic/VirtualFileSystem.cpp b/lib/Basic/VirtualFileSystem.cpp index 822c452b30e..629ae33b8b5 100644 --- a/lib/Basic/VirtualFileSystem.cpp +++ b/lib/Basic/VirtualFileSystem.cpp @@ -114,6 +114,20 @@ bool FileSystem::exists(const Twine &Path) { return Status && Status->exists(); } +#ifndef NDEBUG +static bool isTraversalComponent(StringRef Component) { + return Component.equals("..") || Component.equals("."); +} + +static bool pathHasTraversal(StringRef Path) { + using namespace llvm::sys; + for (StringRef Comp : llvm::make_range(path::begin(Path), path::end(Path))) + if (isTraversalComponent(Comp)) + return true; + return false; +} +#endif + //===-----------------------------------------------------------------------===/ // RealFileSystem implementation //===-----------------------------------------------------------------------===/ @@ -832,6 +846,16 @@ class RedirectingFileSystem : public vfs::FileSystem { bool UseExternalNames; /// @} + /// Virtual file paths and external files could be canonicalized without "..", + /// "." and "./" in their paths. FIXME: some unittests currently fail on + /// win32 when using remove_dots and remove_leading_dotslash on paths. + bool UseCanonicalizedPaths = +#ifdef LLVM_ON_WIN32 + false; +#else + true; +#endif + friend class RedirectingFileSystemParser; private: @@ -967,7 +991,7 @@ class RedirectingFileSystemParser { return true; } - std::unique_ptr parseEntry(yaml::Node *N) { + std::unique_ptr parseEntry(yaml::Node *N, RedirectingFileSystem *FS) { yaml::MappingNode *M = dyn_cast(N); if (!M) { error(N, "expected mapping node for file or directory entry"); @@ -1007,7 +1031,17 @@ class RedirectingFileSystemParser { if (Key == "name") { if (!parseScalarString(I->getValue(), Value, Buffer)) return nullptr; - Name = Value; + + if (FS->UseCanonicalizedPaths) { + SmallString<256> Path(Value); + // Guarantee that old YAML files containing paths with ".." and "." + // are properly canonicalized before read into the VFS. + Path = sys::path::remove_leading_dotslash(Path); + sys::path::remove_dots(Path, /*remove_dot_dot=*/true); + Name = Path.str(); + } else { + Name = Value; + } } else if (Key == "type") { if (!parseScalarString(I->getValue(), Value, Buffer)) return nullptr; @@ -1037,7 +1071,7 @@ class RedirectingFileSystemParser { for (yaml::SequenceNode::iterator I = Contents->begin(), E = Contents->end(); I != E; ++I) { - if (std::unique_ptr E = parseEntry(&*I)) + if (std::unique_ptr E = parseEntry(&*I, FS)) EntryArrayContents.push_back(std::move(E)); else return nullptr; @@ -1051,7 +1085,16 @@ class RedirectingFileSystemParser { HasContents = true; if (!parseScalarString(I->getValue(), Value, Buffer)) return nullptr; - ExternalContentsPath = Value; + if (FS->UseCanonicalizedPaths) { + SmallString<256> Path(Value); + // Guarantee that old YAML files containing paths with ".." and "." + // are properly canonicalized before read into the VFS. + Path = sys::path::remove_leading_dotslash(Path); + sys::path::remove_dots(Path, /*remove_dot_dot=*/true); + ExternalContentsPath = Path.str(); + } else { + ExternalContentsPath = Value; + } } else if (Key == "use-external-name") { bool Val; if (!parseScalarBool(I->getValue(), Val)) @@ -1162,7 +1205,7 @@ class RedirectingFileSystemParser { for (yaml::SequenceNode::iterator I = Roots->begin(), E = Roots->end(); I != E; ++I) { - if (std::unique_ptr E = parseEntry(&*I)) + if (std::unique_ptr E = parseEntry(&*I, FS)) FS->Roots.push_back(std::move(E)); else return false; @@ -1241,6 +1284,14 @@ ErrorOr RedirectingFileSystem::lookupPath(const Twine &Path_) { if (std::error_code EC = makeAbsolute(Path)) return EC; + // Canonicalize path by removing ".", "..", "./", etc components. This is + // a VFS request, do bot bother about symlinks in the path components + // but canonicalize in order to perform the correct entry search. + if (UseCanonicalizedPaths) { + Path = sys::path::remove_leading_dotslash(Path); + sys::path::remove_dots(Path, /*remove_dot_dot=*/true); + } + if (Path.empty()) return make_error_code(llvm::errc::invalid_argument); @@ -1257,10 +1308,17 @@ ErrorOr RedirectingFileSystem::lookupPath(const Twine &Path_) { ErrorOr RedirectingFileSystem::lookupPath(sys::path::const_iterator Start, sys::path::const_iterator End, Entry *From) { +#ifndef LLVM_ON_WIN32 + assert(!isTraversalComponent(*Start) && + !isTraversalComponent(From->getName()) && + "Paths should not contain traversal components"); +#else + // FIXME: this is here to support windows, remove it once canonicalized + // paths become globally default. if (Start->equals(".")) ++Start; +#endif - // FIXME: handle .. if (CaseSensitive ? !Start->equals(From->getName()) : !Start->equals_lower(From->getName())) // failure to match @@ -1379,16 +1437,6 @@ UniqueID vfs::getNextVirtualUniqueID() { return UniqueID(std::numeric_limits::max(), ID); } -#ifndef NDEBUG -static bool pathHasTraversal(StringRef Path) { - using namespace llvm::sys; - for (StringRef Comp : llvm::make_range(path::begin(Path), path::end(Path))) - if (Comp == "." || Comp == "..") - return true; - return false; -} -#endif - void YAMLVFSWriter::addFileMapping(StringRef VirtualPath, StringRef RealPath) { assert(sys::path::is_absolute(VirtualPath) && "virtual path not absolute"); assert(sys::path::is_absolute(RealPath) && "real path not absolute"); diff --git a/lib/Frontend/ModuleDependencyCollector.cpp b/lib/Frontend/ModuleDependencyCollector.cpp index 9768a164acb..8da100980e5 100644 --- a/lib/Frontend/ModuleDependencyCollector.cpp +++ b/lib/Frontend/ModuleDependencyCollector.cpp @@ -13,7 +13,7 @@ #include "clang/Frontend/Utils.h" #include "clang/Serialization/ASTReader.h" -#include "llvm/ADT/StringSet.h" +#include "llvm/ADT/StringMap.h" #include "llvm/ADT/iterator_range.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" @@ -25,7 +25,9 @@ namespace { /// Private implementation for ModuleDependencyCollector class ModuleDependencyListener : public ASTReaderListener { ModuleDependencyCollector &Collector; + llvm::StringMap SymLinkMap; + bool getRealPath(StringRef SrcPath, SmallVectorImpl &Result); std::error_code copyToRoot(StringRef Src); public: ModuleDependencyListener(ModuleDependencyCollector &Collector) @@ -57,6 +59,48 @@ void ModuleDependencyCollector::writeFileMap() { VFSWriter.write(OS); } +// TODO: move this to Support/Path.h and check for HAVE_REALPATH? +static bool real_path(StringRef SrcPath, SmallVectorImpl &RealPath) { +#ifdef LLVM_ON_UNIX + char CanonicalPath[PATH_MAX]; + + // TODO: emit a warning in case this fails...? + if (!realpath(SrcPath.str().c_str(), CanonicalPath)) + return false; + + SmallString<256> RPath(CanonicalPath); + RealPath.swap(RPath); + return true; +#else + // FIXME: Add support for systems without realpath. + return false; +#endif +} + +bool ModuleDependencyListener::getRealPath(StringRef SrcPath, + SmallVectorImpl &Result) { + using namespace llvm::sys; + SmallString<256> RealPath; + StringRef FileName = path::filename(SrcPath); + std::string Dir = path::parent_path(SrcPath).str(); + auto DirWithSymLink = SymLinkMap.find(Dir); + + // Use real_path to fix any symbolic link component present in a path. + // Computing the real path is expensive, cache the search through the + // parent path directory. + if (DirWithSymLink == SymLinkMap.end()) { + if (!real_path(Dir, RealPath)) + return false; + SymLinkMap[Dir] = RealPath.str(); + } else { + RealPath = DirWithSymLink->second; + } + + path::append(RealPath, FileName); + Result.swap(RealPath); + return true; +} + std::error_code ModuleDependencyListener::copyToRoot(StringRef Src) { using namespace llvm::sys; @@ -65,22 +109,42 @@ std::error_code ModuleDependencyListener::copyToRoot(StringRef Src) { fs::make_absolute(AbsoluteSrc); // Canonicalize to a native path to avoid mixed separator styles. path::native(AbsoluteSrc); - // TODO: We probably need to handle .. as well as . in order to have valid - // input to the YAMLVFSWriter. - path::remove_dots(AbsoluteSrc); + // Remove redundant leading "./" pieces and consecutive separators. + AbsoluteSrc = path::remove_leading_dotslash(AbsoluteSrc); + + // Canonicalize path by removing "..", "." components. + SmallString<256> CanonicalPath = AbsoluteSrc; + path::remove_dots(CanonicalPath, /*remove_dot_dot=*/true); + + // If a ".." component is present after a symlink component, remove_dots may + // lead to the wrong real destination path. Let the source be canonicalized + // like that but make sure the destination uses the real path. + bool HasDotDotInPath = + std::count(path::begin(AbsoluteSrc), path::end(AbsoluteSrc), "..") > 0; + SmallString<256> RealPath; + bool HasRemovedSymlinkComponent = HasDotDotInPath && + getRealPath(AbsoluteSrc, RealPath) && + !StringRef(CanonicalPath).equals(RealPath); // Build the destination path. SmallString<256> Dest = Collector.getDest(); - path::append(Dest, path::relative_path(AbsoluteSrc)); + path::append(Dest, path::relative_path(HasRemovedSymlinkComponent ? RealPath + : CanonicalPath)); // Copy the file into place. if (std::error_code EC = fs::create_directories(path::parent_path(Dest), /*IgnoreExisting=*/true)) return EC; - if (std::error_code EC = fs::copy_file(AbsoluteSrc, Dest)) + if (std::error_code EC = fs::copy_file( + HasRemovedSymlinkComponent ? RealPath : CanonicalPath, Dest)) return EC; - // Use the absolute path under the root for the file mapping. - Collector.addFileMapping(AbsoluteSrc, Dest); + + // Use the canonical path under the root for the file mapping. Also create + // an additional entry for the real path. + Collector.addFileMapping(CanonicalPath, Dest); + if (HasRemovedSymlinkComponent) + Collector.addFileMapping(RealPath, Dest); + return std::error_code(); } diff --git a/test/Modules/Inputs/crash-recovery/usr/include/module.map b/test/Modules/Inputs/crash-recovery/usr/include/module.map new file mode 100644 index 00000000000..70aae494eff --- /dev/null +++ b/test/Modules/Inputs/crash-recovery/usr/include/module.map @@ -0,0 +1,6 @@ +module cstd [system] { + // Only in system headers directory + module stdio { + header "stdio.h" + } +} diff --git a/test/Modules/Inputs/crash-recovery/usr/include/stdio.h b/test/Modules/Inputs/crash-recovery/usr/include/stdio.h new file mode 100644 index 00000000000..f41e09c35a4 --- /dev/null +++ b/test/Modules/Inputs/crash-recovery/usr/include/stdio.h @@ -0,0 +1,3 @@ +typedef struct { int id; } FILE; +int fprintf(FILE*restrict, const char* restrict format, ...); +extern FILE *__stderrp; diff --git a/test/Modules/Inputs/crash-recovery/usr/include/tcl-private/header.h b/test/Modules/Inputs/crash-recovery/usr/include/tcl-private/header.h new file mode 100644 index 00000000000..0e8fb64a712 --- /dev/null +++ b/test/Modules/Inputs/crash-recovery/usr/include/tcl-private/header.h @@ -0,0 +1,2 @@ +// tcl-private/header.h +#define TCL_PRIVATE 1 diff --git a/test/Modules/crash-vfs-path-symlink-component.m b/test/Modules/crash-vfs-path-symlink-component.m new file mode 100644 index 00000000000..e27e719efb7 --- /dev/null +++ b/test/Modules/crash-vfs-path-symlink-component.m @@ -0,0 +1,72 @@ +// REQUIRES: crash-recovery, shell + +// FIXME: This XFAIL is cargo-culted from crash-report.c. Do we need it? +// XFAIL: mingw32 + +// Test that clang is capable of collecting the right header files in the +// crash reproducer if there's a symbolic link component in the path. + +// RUN: rm -rf %t +// RUN: mkdir -p %t/i %t/m %t %t/sysroot +// RUN: cp -a %S/Inputs/crash-recovery/usr %t/i/ +// RUN: ln -s include/tcl-private %t/i/usr/x + +// RUN: not env FORCE_CLANG_DIAGNOSTICS_CRASH= TMPDIR=%t TEMP=%t TMP=%t \ +// RUN: %clang -fsyntax-only %s -I %/t/i -isysroot %/t/sysroot/ \ +// RUN: -fmodules -fmodules-cache-path=%t/m/ 2>&1 | FileCheck %s + +// RUN: FileCheck --check-prefix=CHECKSRC %s -input-file %t/crash-vfs-*.m +// RUN: FileCheck --check-prefix=CHECKSH %s -input-file %t/crash-vfs-*.sh +// RUN: FileCheck --check-prefix=CHECKYAML %s -input-file \ +// RUN: %t/crash-vfs-*.cache/vfs/vfs.yaml +// RUN: find %t/crash-vfs-*.cache/vfs | \ +// RUN: grep "usr/include/stdio.h" | count 1 + +#include "usr/x/../stdio.h" + +// CHECK: Preprocessed source(s) and associated run script(s) are located at: +// CHECK-NEXT: note: diagnostic msg: {{.*}}.m +// CHECK-NEXT: note: diagnostic msg: {{.*}}.cache + +// CHECKSRC: @import cstd.stdio; + +// CHECKSH: # Crash reproducer +// CHECKSH-NEXT: # Driver args: "-fsyntax-only" +// CHECKSH-NEXT: # Original command: {{.*$}} +// CHECKSH-NEXT: "-cc1" +// CHECKSH: "-isysroot" "{{[^"]*}}/sysroot/" +// CHECKSH-NOT: "-fmodules-cache-path=" +// CHECKSH: "crash-vfs-{{[^ ]*}}.m" +// CHECKSH: "-ivfsoverlay" "crash-vfs-{{[^ ]*}}.cache/vfs/vfs.yaml" + +// CHECKYAML: 'type': 'directory' +// CHECKYAML: 'name': "{{[^ ]*}}/i/usr/include", +// CHECKYAML-NEXT: 'contents': [ +// CHECKYAML-NEXT: { +// CHECKYAML-NEXT: 'type': 'file', +// CHECKYAML-NEXT: 'name': "module.map", +// CHECKYAML-NEXT: 'external-contents': "{{[^ ]*}}.cache/vfs/{{[^ ]*}}/i/usr/include/module.map" +// CHECKYAML-NEXT: }, + +// CHECKYAML: 'type': 'directory' +// CHECKYAML: 'name': "{{[^ ]*}}/i/usr", +// CHECKYAML-NEXT: 'contents': [ +// CHECKYAML-NEXT: { +// CHECKYAML-NEXT: 'type': 'file', +// CHECKYAML-NEXT: 'name': "module.map", +// CHECKYAML-NEXT: 'external-contents': "{{[^ ]*}}.cache/vfs/{{[^ ]*}}/i/usr/include/module.map" +// CHECKYAML-NEXT: }, + +// Test that by using the previous generated YAML file clang is able to find the +// right files inside the overlay and map the virtual request for a path that +// previously contained a symlink to work. To make sure of this, wipe out the +// %/t/i directory containing the symlink component. + +// RUN: rm -rf %/t/i +// RUN: unset FORCE_CLANG_DIAGNOSTICS_CRASH +// RUN: %clang -E %s -I %/t/i -isysroot %/t/sysroot/ \ +// RUN: -ivfsoverlay %t/crash-vfs-*.cache/vfs/vfs.yaml -fmodules \ +// RUN: -fmodules-cache-path=%t/m/ 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECKOVERLAY + +// CHECKOVERLAY: @import cstd.stdio; /* clang -E: implicit import for "{{[^ ]*}}.cache/vfs/{{[^ ]*}}/i/usr/include/stdio.h" diff --git a/test/Modules/crash-vfs-path-traversal.m b/test/Modules/crash-vfs-path-traversal.m new file mode 100644 index 00000000000..b8da86007fc --- /dev/null +++ b/test/Modules/crash-vfs-path-traversal.m @@ -0,0 +1,60 @@ +// REQUIRES: crash-recovery, shell, non-ms-sdk, non-ps4-sdk + +// FIXME: Canonicalizing paths to remove relative traversal components +// currenty fails a unittest on windows and is disable by default. +// FIXME: This XFAIL is cargo-culted from crash-report.c. Do we need it? +// XFAIL: mingw32 + +// RUN: rm -rf %t +// RUN: mkdir -p %t/i %t/m %t + +// RUN: not env FORCE_CLANG_DIAGNOSTICS_CRASH= TMPDIR=%t TEMP=%t TMP=%t \ +// RUN: %clang -fsyntax-only %s -I %S/Inputs/crash-recovery -isysroot %/t/i/ \ +// RUN: -fmodules -fmodules-cache-path=%t/m/ 2>&1 | FileCheck %s + +// RUN: FileCheck --check-prefix=CHECKSRC %s -input-file %t/crash-vfs-*.m +// RUN: FileCheck --check-prefix=CHECKSH %s -input-file %t/crash-vfs-*.sh +// RUN: FileCheck --check-prefix=CHECKYAML %s -input-file \ +// RUN: %t/crash-vfs-*.cache/vfs/vfs.yaml +// RUN: find %t/crash-vfs-*.cache/vfs | \ +// RUN: grep "Inputs/crash-recovery/usr/include/stdio.h" | count 1 + +#include "usr/././//////include/../include/./././../include/stdio.h" + +// CHECK: Preprocessed source(s) and associated run script(s) are located at: +// CHECK-NEXT: note: diagnostic msg: {{.*}}.m +// CHECK-NEXT: note: diagnostic msg: {{.*}}.cache + +// CHECKSRC: @import cstd.stdio; + +// CHECKSH: # Crash reproducer +// CHECKSH-NEXT: # Driver args: "-fsyntax-only" +// CHECKSH-NEXT: # Original command: {{.*$}} +// CHECKSH-NEXT: "-cc1" +// CHECKSH: "-isysroot" "{{[^"]*}}/i/" +// CHECKSH-NOT: "-fmodules-cache-path=" +// CHECKSH: "crash-vfs-{{[^ ]*}}.m" +// CHECKSH: "-ivfsoverlay" "crash-vfs-{{[^ ]*}}.cache/vfs/vfs.yaml" + +// CHECKYAML: 'type': 'directory' +// CHECKYAML: 'name': "{{[^ ]*}}/Inputs/crash-recovery/usr/include", +// CHECKYAML-NEXT: 'contents': [ +// CHECKYAML-NEXT: { +// CHECKYAML-NEXT: 'type': 'file', +// CHECKYAML-NEXT: 'name': "module.map", +// CHECKYAML-NEXT: 'external-contents': "{{[^ ]*}}/Inputs/crash-recovery/usr/include/module.map" +// CHECKYAML-NEXT: }, + +// Replace the paths in the YAML files with relative ".." traversals +// and fed into clang to test whether we're correctly representing them +// in the VFS overlay. + +// RUN: sed -e "s@usr/include@usr/include/../include@g" \ +// RUN: %t/crash-vfs-*.cache/vfs/vfs.yaml > %t/vfs.yaml +// RUN: unset FORCE_CLANG_DIAGNOSTICS_CRASH +// RUN: %clang -E %s -I %S/Inputs/crash-recovery -isysroot %/t/i/ \ +// RUN: -ivfsoverlay %t/vfs.yaml -fmodules \ +// RUN: -fmodules-cache-path=%t/m/ 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECKOVERLAY + +// CHECKOVERLAY: @import cstd.stdio; /* clang -E: implicit import for "{{[^ ]*}}.cache/vfs/{{[^ ]*}}/usr/include/stdio.h" From d99463d129f329ddc71de1424a62948c90a5f6f8 Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes Date: Sun, 20 Mar 2016 02:08:48 +0000 Subject: [PATCH 484/742] [VFS] Add 'overlay-relative' field to YAML files This reapplies r261552 and r263748. Fixed testcase to reapply. The VFS overlay mapping between virtual paths and real paths is done through the 'external-contents' entries in YAML files, which contains hardcoded paths to the real files. When a module compilation crashes, headers are dumped into .cache/vfs directory and are mapped via the .cache/vfs/vfs.yaml. The script generated for reproduction uses -ivfsoverlay pointing to file to gather the mapping between virtual paths and files inside .cache/vfs. Currently, we are only capable of reproducing such crashes in the same machine as they happen, because of the hardcoded paths in 'external-contents'. To be able to reproduce a crash in another machine, this patch introduces a new option in the VFS yaml file called 'overlay-relative'. When it's equal to 'true' it means that the provided path to the YAML file through the -ivfsoverlay option should also be used to prefix the final path for every 'external-contents'. Example, given the invocation snippet "... -ivfsoverlay .cache/vfs/vfs.yaml" and the following entry in the yaml file: "overlay-relative": "true", "roots": [ ... "type": "directory", "name": "/usr/include", "contents": [ { "type": "file", "name": "stdio.h", "external-contents": "/usr/include/stdio.h" }, ... Here, a file manager request for virtual "/usr/include/stdio.h", that will map into real path "//.cache/vfs/usr/include/stdio.h. This is a useful feature for debugging module crashes in machines other than the one where the error happened. Differential Revision: http://reviews.llvm.org/D17457 rdar://problem/24499339 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263893 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 86240eff14fb1ddf5bd68cca062f7048b3c04847) git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263912 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 26d94682bce93b81ae375874df73881438b8dfd8) --- include/clang/Basic/VirtualFileSystem.h | 8 ++ lib/Basic/VirtualFileSystem.cpp | 113 +++++++++++++++--- lib/Frontend/CompilerInvocation.cpp | 4 +- lib/Frontend/ModuleDependencyCollector.cpp | 4 + .../crash-vfs-path-symlink-component.m | 8 +- test/Modules/crash-vfs-path-traversal.m | 9 +- test/Modules/crash-vfs-relative-overlay.m | 56 +++++++++ unittests/Basic/VirtualFileSystemTest.cpp | 2 +- 8 files changed, 175 insertions(+), 29 deletions(-) create mode 100644 test/Modules/crash-vfs-relative-overlay.m diff --git a/include/clang/Basic/VirtualFileSystem.h b/include/clang/Basic/VirtualFileSystem.h index bab88c90b0a..cc13d72a5b4 100644 --- a/include/clang/Basic/VirtualFileSystem.h +++ b/include/clang/Basic/VirtualFileSystem.h @@ -310,6 +310,7 @@ llvm::sys::fs::UniqueID getNextVirtualUniqueID(); IntrusiveRefCntPtr getVFSFromYAML(std::unique_ptr Buffer, llvm::SourceMgr::DiagHandlerTy DiagHandler, + StringRef YAMLFilePath, void *DiagContext = nullptr, IntrusiveRefCntPtr ExternalFS = getRealFileSystem()); @@ -323,6 +324,8 @@ struct YAMLVFSEntry { class YAMLVFSWriter { std::vector Mappings; Optional IsCaseSensitive; + Optional IsOverlayRelative; + std::string OverlayDir; public: YAMLVFSWriter() {} @@ -330,6 +333,11 @@ class YAMLVFSWriter { void setCaseSensitivity(bool CaseSensitive) { IsCaseSensitive = CaseSensitive; } + void setOverlayDir(StringRef OverlayDirectory) { + IsOverlayRelative = true; + OverlayDir.assign(OverlayDirectory.str()); + } + void write(llvm::raw_ostream &OS); }; diff --git a/lib/Basic/VirtualFileSystem.cpp b/lib/Basic/VirtualFileSystem.cpp index 629ae33b8b5..95f6dc82468 100644 --- a/lib/Basic/VirtualFileSystem.cpp +++ b/lib/Basic/VirtualFileSystem.cpp @@ -793,6 +793,7 @@ class VFSFromYamlDirIterImpl : public clang::vfs::detail::DirIterImpl { /// All configuration options are optional. /// 'case-sensitive': /// 'use-external-names': +/// 'overlay-relative': /// /// Virtual directories are represented as /// \verbatim @@ -832,6 +833,10 @@ class RedirectingFileSystem : public vfs::FileSystem { std::vector> Roots; /// \brief The file system to use for external references. IntrusiveRefCntPtr ExternalFS; + /// If IsRelativeOverlay is set, this represents the directory + /// path that should be prefixed to each 'external-contents' entry + /// when reading from YAML files. + std::string ExternalContentsPrefixDir; /// @name Configuration /// @{ @@ -841,6 +846,10 @@ class RedirectingFileSystem : public vfs::FileSystem { /// Currently, case-insensitive matching only works correctly with ASCII. bool CaseSensitive; + /// IsRelativeOverlay marks whether a IsExternalContentsPrefixDir path must + /// be prefixed in every 'external-contents' when reading from YAML files. + bool IsRelativeOverlay = false; + /// \brief Whether to use to use the value of 'external-contents' for the /// names of files. This global value is overridable on a per-file basis. bool UseExternalNames; @@ -878,8 +887,8 @@ class RedirectingFileSystem : public vfs::FileSystem { /// returns a virtual file system representing its contents. static RedirectingFileSystem * create(std::unique_ptr Buffer, - SourceMgr::DiagHandlerTy DiagHandler, void *DiagContext, - IntrusiveRefCntPtr ExternalFS); + SourceMgr::DiagHandlerTy DiagHandler, StringRef YAMLFilePath, + void *DiagContext, IntrusiveRefCntPtr ExternalFS); ErrorOr status(const Twine &Path) override; ErrorOr> openFileForRead(const Twine &Path) override; @@ -912,6 +921,15 @@ class RedirectingFileSystem : public vfs::FileSystem { return directory_iterator(std::make_shared(Dir, *this, D->contents_begin(), D->contents_end(), EC)); } + + void setExternalContentsPrefixDir(StringRef PrefixDir) { + ExternalContentsPrefixDir = PrefixDir.str(); + } + + StringRef getExternalContentsPrefixDir() const { + return ExternalContentsPrefixDir; + } + }; /// \brief A helper class to hold the common YAML parsing state. @@ -1085,16 +1103,24 @@ class RedirectingFileSystemParser { HasContents = true; if (!parseScalarString(I->getValue(), Value, Buffer)) return nullptr; + + SmallString<256> FullPath; + if (FS->IsRelativeOverlay) { + FullPath = FS->getExternalContentsPrefixDir(); + assert(!FullPath.empty() && + "External contents prefix directory must exist"); + llvm::sys::path::append(FullPath, Value); + } else { + FullPath = Value; + } + if (FS->UseCanonicalizedPaths) { - SmallString<256> Path(Value); // Guarantee that old YAML files containing paths with ".." and "." // are properly canonicalized before read into the VFS. - Path = sys::path::remove_leading_dotslash(Path); - sys::path::remove_dots(Path, /*remove_dot_dot=*/true); - ExternalContentsPath = Path.str(); - } else { - ExternalContentsPath = Value; + FullPath = sys::path::remove_leading_dotslash(FullPath); + sys::path::remove_dots(FullPath, /*remove_dot_dot=*/true); } + ExternalContentsPath = FullPath.str(); } else if (Key == "use-external-name") { bool Val; if (!parseScalarBool(I->getValue(), Val)) @@ -1180,6 +1206,7 @@ class RedirectingFileSystemParser { KeyStatusPair("version", true), KeyStatusPair("case-sensitive", false), KeyStatusPair("use-external-names", false), + KeyStatusPair("overlay-relative", false), KeyStatusPair("roots", true), }; @@ -1231,6 +1258,9 @@ class RedirectingFileSystemParser { } else if (Key == "case-sensitive") { if (!parseScalarBool(I->getValue(), FS->CaseSensitive)) return false; + } else if (Key == "overlay-relative") { + if (!parseScalarBool(I->getValue(), FS->IsRelativeOverlay)) + return false; } else if (Key == "use-external-names") { if (!parseScalarBool(I->getValue(), FS->UseExternalNames)) return false; @@ -1251,9 +1281,11 @@ class RedirectingFileSystemParser { Entry::~Entry() = default; -RedirectingFileSystem *RedirectingFileSystem::create( - std::unique_ptr Buffer, SourceMgr::DiagHandlerTy DiagHandler, - void *DiagContext, IntrusiveRefCntPtr ExternalFS) { +RedirectingFileSystem * +RedirectingFileSystem::create(std::unique_ptr Buffer, + SourceMgr::DiagHandlerTy DiagHandler, + StringRef YAMLFilePath, void *DiagContext, + IntrusiveRefCntPtr ExternalFS) { SourceMgr SM; yaml::Stream Stream(Buffer->getMemBufferRef(), SM); @@ -1270,6 +1302,23 @@ RedirectingFileSystem *RedirectingFileSystem::create( std::unique_ptr FS( new RedirectingFileSystem(ExternalFS)); + + if (!YAMLFilePath.empty()) { + // Use the YAML path from -ivfsoverlay to compute the dir to be prefixed + // to each 'external-contents' path. + // + // Example: + // -ivfsoverlay dummy.cache/vfs/vfs.yaml + // yields: + // FS->ExternalContentsPrefixDir => //dummy.cache/vfs + // + SmallString<256> OverlayAbsDir = sys::path::parent_path(YAMLFilePath); + std::error_code EC = llvm::sys::fs::make_absolute(OverlayAbsDir); + assert(!EC && "Overlay dir final path must be absolute"); + (void)EC; + FS->setExternalContentsPrefixDir(OverlayAbsDir); + } + if (!P.parse(Root, FS.get())) return nullptr; @@ -1423,10 +1472,12 @@ RedirectingFileSystem::openFileForRead(const Twine &Path) { IntrusiveRefCntPtr vfs::getVFSFromYAML(std::unique_ptr Buffer, - SourceMgr::DiagHandlerTy DiagHandler, void *DiagContext, + SourceMgr::DiagHandlerTy DiagHandler, + StringRef YAMLFilePath, + void *DiagContext, IntrusiveRefCntPtr ExternalFS) { return RedirectingFileSystem::create(std::move(Buffer), DiagHandler, - DiagContext, ExternalFS); + YAMLFilePath, DiagContext, ExternalFS); } UniqueID vfs::getNextVirtualUniqueID() { @@ -1458,7 +1509,8 @@ class JSONWriter { public: JSONWriter(llvm::raw_ostream &OS) : OS(OS) {} - void write(ArrayRef Entries, Optional IsCaseSensitive); + void write(ArrayRef Entries, Optional IsCaseSensitive, + Optional IsOverlayRelative, StringRef OverlayDir); }; } @@ -1511,7 +1563,9 @@ void JSONWriter::writeEntry(StringRef VPath, StringRef RPath) { } void JSONWriter::write(ArrayRef Entries, - Optional IsCaseSensitive) { + Optional IsCaseSensitive, + Optional IsOverlayRelative, + StringRef OverlayDir) { using namespace llvm::sys; OS << "{\n" @@ -1519,12 +1573,27 @@ void JSONWriter::write(ArrayRef Entries, if (IsCaseSensitive.hasValue()) OS << " 'case-sensitive': '" << (IsCaseSensitive.getValue() ? "true" : "false") << "',\n"; + bool UseOverlayRelative = false; + if (IsOverlayRelative.hasValue()) { + UseOverlayRelative = IsOverlayRelative.getValue(); + OS << " 'overlay-relative': '" + << (UseOverlayRelative ? "true" : "false") << "',\n"; + } OS << " 'roots': [\n"; if (!Entries.empty()) { const YAMLVFSEntry &Entry = Entries.front(); startDirectory(path::parent_path(Entry.VPath)); - writeEntry(path::filename(Entry.VPath), Entry.RPath); + + StringRef RPath = Entry.RPath; + if (UseOverlayRelative) { + unsigned OverlayDirLen = OverlayDir.size(); + assert(RPath.substr(0, OverlayDirLen) == OverlayDir && + "Overlay dir must be contained in RPath"); + RPath = RPath.slice(OverlayDirLen, RPath.size()); + } + + writeEntry(path::filename(Entry.VPath), RPath); for (const auto &Entry : Entries.slice(1)) { StringRef Dir = path::parent_path(Entry.VPath); @@ -1538,7 +1607,14 @@ void JSONWriter::write(ArrayRef Entries, OS << ",\n"; startDirectory(Dir); } - writeEntry(path::filename(Entry.VPath), Entry.RPath); + StringRef RPath = Entry.RPath; + if (UseOverlayRelative) { + unsigned OverlayDirLen = OverlayDir.size(); + assert(RPath.substr(0, OverlayDirLen) == OverlayDir && + "Overlay dir must be contained in RPath"); + RPath = RPath.slice(OverlayDirLen, RPath.size()); + } + writeEntry(path::filename(Entry.VPath), RPath); } while (!DirStack.empty()) { @@ -1558,7 +1634,8 @@ void YAMLVFSWriter::write(llvm::raw_ostream &OS) { return LHS.VPath < RHS.VPath; }); - JSONWriter(OS).write(Mappings, IsCaseSensitive); + JSONWriter(OS).write(Mappings, IsCaseSensitive, IsOverlayRelative, + OverlayDir); } VFSFromYamlDirIterImpl::VFSFromYamlDirIterImpl( diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index a8329a4fbed..52e981ada19 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -2329,8 +2329,8 @@ createVFSFromCompilerInvocation(const CompilerInvocation &CI, return IntrusiveRefCntPtr(); } - IntrusiveRefCntPtr FS = - vfs::getVFSFromYAML(std::move(Buffer.get()), /*DiagHandler*/ nullptr); + IntrusiveRefCntPtr FS = vfs::getVFSFromYAML( + std::move(Buffer.get()), /*DiagHandler*/ nullptr, File); if (!FS.get()) { Diags.Report(diag::err_invalid_vfs_overlay) << File; return IntrusiveRefCntPtr(); diff --git a/lib/Frontend/ModuleDependencyCollector.cpp b/lib/Frontend/ModuleDependencyCollector.cpp index 8da100980e5..1f187b12604 100644 --- a/lib/Frontend/ModuleDependencyCollector.cpp +++ b/lib/Frontend/ModuleDependencyCollector.cpp @@ -50,6 +50,10 @@ void ModuleDependencyCollector::writeFileMap() { SmallString<256> Dest = getDest(); llvm::sys::path::append(Dest, "vfs.yaml"); + // Default to use relative overlay directories in the VFS yaml file. This + // allows crash reproducer scripts to work across machines. + VFSWriter.setOverlayDir(getDest()); + std::error_code EC; llvm::raw_fd_ostream OS(Dest, EC, llvm::sys::fs::F_Text); if (EC) { diff --git a/test/Modules/crash-vfs-path-symlink-component.m b/test/Modules/crash-vfs-path-symlink-component.m index e27e719efb7..9687cc9a15f 100644 --- a/test/Modules/crash-vfs-path-symlink-component.m +++ b/test/Modules/crash-vfs-path-symlink-component.m @@ -40,21 +40,21 @@ // CHECKSH: "-ivfsoverlay" "crash-vfs-{{[^ ]*}}.cache/vfs/vfs.yaml" // CHECKYAML: 'type': 'directory' -// CHECKYAML: 'name': "{{[^ ]*}}/i/usr/include", +// CHECKYAML: 'name': "/[[PATH:.*]]/i/usr/include", // CHECKYAML-NEXT: 'contents': [ // CHECKYAML-NEXT: { // CHECKYAML-NEXT: 'type': 'file', // CHECKYAML-NEXT: 'name': "module.map", -// CHECKYAML-NEXT: 'external-contents': "{{[^ ]*}}.cache/vfs/{{[^ ]*}}/i/usr/include/module.map" +// CHECKYAML-NEXT: 'external-contents': "/[[PATH]]/i/usr/include/module.map" // CHECKYAML-NEXT: }, // CHECKYAML: 'type': 'directory' -// CHECKYAML: 'name': "{{[^ ]*}}/i/usr", +// CHECKYAML: 'name': "/[[PATH]]/i/usr", // CHECKYAML-NEXT: 'contents': [ // CHECKYAML-NEXT: { // CHECKYAML-NEXT: 'type': 'file', // CHECKYAML-NEXT: 'name': "module.map", -// CHECKYAML-NEXT: 'external-contents': "{{[^ ]*}}.cache/vfs/{{[^ ]*}}/i/usr/include/module.map" +// CHECKYAML-NEXT: 'external-contents': "/[[PATH]]/i/usr/include/module.map" // CHECKYAML-NEXT: }, // Test that by using the previous generated YAML file clang is able to find the diff --git a/test/Modules/crash-vfs-path-traversal.m b/test/Modules/crash-vfs-path-traversal.m index b8da86007fc..60e73d4fb90 100644 --- a/test/Modules/crash-vfs-path-traversal.m +++ b/test/Modules/crash-vfs-path-traversal.m @@ -36,13 +36,13 @@ // CHECKSH: "crash-vfs-{{[^ ]*}}.m" // CHECKSH: "-ivfsoverlay" "crash-vfs-{{[^ ]*}}.cache/vfs/vfs.yaml" -// CHECKYAML: 'type': 'directory' -// CHECKYAML: 'name': "{{[^ ]*}}/Inputs/crash-recovery/usr/include", +// CHECKYAML: 'type': 'directory' +// CHECKYAML: 'name': "/[[PATH:.*]]/Inputs/crash-recovery/usr/include", // CHECKYAML-NEXT: 'contents': [ // CHECKYAML-NEXT: { // CHECKYAML-NEXT: 'type': 'file', // CHECKYAML-NEXT: 'name': "module.map", -// CHECKYAML-NEXT: 'external-contents': "{{[^ ]*}}/Inputs/crash-recovery/usr/include/module.map" +// CHECKYAML-NEXT: 'external-contents': "/[[PATH]]/Inputs/crash-recovery/usr/include/module.map" // CHECKYAML-NEXT: }, // Replace the paths in the YAML files with relative ".." traversals @@ -51,9 +51,10 @@ // RUN: sed -e "s@usr/include@usr/include/../include@g" \ // RUN: %t/crash-vfs-*.cache/vfs/vfs.yaml > %t/vfs.yaml +// RUN: cp %t/vfs.yaml %t/crash-vfs-*.cache/vfs/vfs.yaml // RUN: unset FORCE_CLANG_DIAGNOSTICS_CRASH // RUN: %clang -E %s -I %S/Inputs/crash-recovery -isysroot %/t/i/ \ -// RUN: -ivfsoverlay %t/vfs.yaml -fmodules \ +// RUN: -ivfsoverlay %t/crash-vfs-*.cache/vfs/vfs.yaml -fmodules \ // RUN: -fmodules-cache-path=%t/m/ 2>&1 \ // RUN: | FileCheck %s --check-prefix=CHECKOVERLAY diff --git a/test/Modules/crash-vfs-relative-overlay.m b/test/Modules/crash-vfs-relative-overlay.m new file mode 100644 index 00000000000..d4e1e0d3dfa --- /dev/null +++ b/test/Modules/crash-vfs-relative-overlay.m @@ -0,0 +1,56 @@ +// REQUIRES: crash-recovery, shell + +// FIXME: This XFAIL is cargo-culted from crash-report.c. Do we need it? +// XFAIL: mingw32 + +// RUN: rm -rf %t +// RUN: mkdir -p %t/i %t/m %t + +// RUN: not env FORCE_CLANG_DIAGNOSTICS_CRASH= TMPDIR=%t TEMP=%t TMP=%t \ +// RUN: %clang -fsyntax-only %s -I %S/Inputs/crash-recovery -isysroot %/t/i/ \ +// RUN: -fmodules -fmodules-cache-path=%t/m/ 2>&1 | FileCheck %s + +// RUN: FileCheck --check-prefix=CHECKSRC %s -input-file %t/crash-vfs-*.m +// RUN: FileCheck --check-prefix=CHECKSH %s -input-file %t/crash-vfs-*.sh +// RUN: FileCheck --check-prefix=CHECKYAML %s -input-file \ +// RUN: %t/crash-vfs-*.cache/vfs/vfs.yaml +// RUN: find %t/crash-vfs-*.cache/vfs | \ +// RUN: grep "Inputs/crash-recovery/usr/include/stdio.h" | count 1 + +#include "usr/include/stdio.h" + +// CHECK: Preprocessed source(s) and associated run script(s) are located at: +// CHECK-NEXT: note: diagnostic msg: {{.*}}.m +// CHECK-NEXT: note: diagnostic msg: {{.*}}.cache + +// CHECKSRC: @import cstd.stdio; + +// CHECKSH: # Crash reproducer +// CHECKSH-NEXT: # Driver args: "-fsyntax-only" +// CHECKSH-NEXT: # Original command: {{.*$}} +// CHECKSH-NEXT: "-cc1" +// CHECKSH: "-isysroot" "{{[^"]*}}/i/" +// CHECKSH-NOT: "-fmodules-cache-path=" +// CHECKSH: "crash-vfs-{{[^ ]*}}.m" +// CHECKSH: "-ivfsoverlay" "crash-vfs-{{[^ ]*}}.cache/vfs/vfs.yaml" + +// CHECKYAML: 'type': 'directory' +// CHECKYAML: 'name': "/[[PATH:.*]]/Inputs/crash-recovery/usr/include", +// CHECKYAML-NEXT: 'contents': [ +// CHECKYAML-NEXT: { +// CHECKYAML-NEXT: 'type': 'file', +// CHECKYAML-NEXT: 'name': "module.map", +// CHECKYAML-NOT: 'external-contents': "{{[^ ]*}}.cache +// CHECKYAML-NEXT: 'external-contents': "/[[PATH]]/Inputs/crash-recovery/usr/include/module.map" +// CHECKYAML-NEXT: }, + +// Test that reading the YAML file will yield the correct path after +// the overlay dir is prefixed to access headers in .cache/vfs directory. + +// RUN: unset FORCE_CLANG_DIAGNOSTICS_CRASH +// RUN: %clang -E %s -I %S/Inputs/crash-recovery -isysroot %/t/i/ \ +// RUN: -ivfsoverlay %t/crash-vfs-*.cache/vfs/vfs.yaml -fmodules \ +// RUN: -fmodules-cache-path=%t/m/ 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECKOVERLAY + +// CHECKOVERLAY: @import cstd.stdio; /* clang -E: implicit import for "{{[^ ]*}}.cache/vfs/{{[^ ]*}}/usr/include/stdio.h" diff --git a/unittests/Basic/VirtualFileSystemTest.cpp b/unittests/Basic/VirtualFileSystemTest.cpp index 7abc549292e..0ca4b08bc35 100644 --- a/unittests/Basic/VirtualFileSystemTest.cpp +++ b/unittests/Basic/VirtualFileSystemTest.cpp @@ -663,7 +663,7 @@ class VFSFromYAMLTest : public ::testing::Test { getFromYAMLRawString(StringRef Content, IntrusiveRefCntPtr ExternalFS) { std::unique_ptr Buffer = MemoryBuffer::getMemBuffer(Content); - return getVFSFromYAML(std::move(Buffer), CountingDiagHandler, this, + return getVFSFromYAML(std::move(Buffer), CountingDiagHandler, "", this, ExternalFS); } From 664154032b3b15567114fbcf190a7aa3a61206cb Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes Date: Tue, 29 Mar 2016 23:47:40 +0000 Subject: [PATCH 485/742] [CrashReproducer] Cleanup and move functionality around in ModuleDependencyCollector. NFC - Make ModuleDependencyCollector use the DependencyCollector interface - Move some methods from ModuleDependencyListener to ModuleDependencyCollector in order to share common functionality with other future possible callbacks. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@264808 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit b08ac6414c3605f6bb92fd73bb16c862d611c34c) --- include/clang/Frontend/Utils.h | 22 +++++++++------ lib/Frontend/CompilerInstance.cpp | 14 ++++++---- lib/Frontend/ModuleDependencyCollector.cpp | 32 ++++++++++------------ 3 files changed, 35 insertions(+), 33 deletions(-) diff --git a/include/clang/Frontend/Utils.h b/include/clang/Frontend/Utils.h index a5f667ef7c7..10767a617c9 100644 --- a/include/clang/Frontend/Utils.h +++ b/include/clang/Frontend/Utils.h @@ -73,12 +73,12 @@ void DoPrintPreprocessedInput(Preprocessor &PP, raw_ostream* OS, /// An interface for collecting the dependencies of a compilation. Users should /// use \c attachToPreprocessor and \c attachToASTReader to get all of the /// dependencies. -// FIXME: Migrate DependencyFileGen, DependencyGraphGen, ModuleDepCollectory to -// use this interface. +/// FIXME: Migrate DependencyFileGen and DependencyGraphGen to use this +/// interface. class DependencyCollector { public: void attachToPreprocessor(Preprocessor &PP); - void attachToASTReader(ASTReader &R); + virtual void attachToASTReader(ASTReader &R); llvm::ArrayRef getDependencies() const { return Dependencies; } /// Called when a new file is seen. Return true if \p Filename should be added @@ -118,25 +118,29 @@ class DependencyFileGenerator { /// Collects the dependencies for imported modules into a directory. Users /// should attach to the AST reader whenever a module is loaded. -class ModuleDependencyCollector { +class ModuleDependencyCollector : public DependencyCollector { std::string DestDir; - bool HasErrors; + bool HasErrors = false; llvm::StringSet<> Seen; vfs::YAMLVFSWriter VFSWriter; + llvm::StringMap SymLinkMap; + + bool getRealPath(StringRef SrcPath, SmallVectorImpl &Result); + std::error_code copyToRoot(StringRef Src); public: StringRef getDest() { return DestDir; } bool insertSeen(StringRef Filename) { return Seen.insert(Filename).second; } - void setHasErrors() { HasErrors = true; } + void addFile(StringRef Filename); void addFileMapping(StringRef VPath, StringRef RPath) { VFSWriter.addFileMapping(VPath, RPath); } - void attachToASTReader(ASTReader &R); + void attachToASTReader(ASTReader &R) override; + void writeFileMap(); bool hasErrors() { return HasErrors; } - ModuleDependencyCollector(std::string DestDir) - : DestDir(DestDir), HasErrors(false) {} + ModuleDependencyCollector(std::string DestDir) : DestDir(DestDir) {} ~ModuleDependencyCollector() { writeFileMap(); } }; diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp index ae34582df05..fcea647f262 100644 --- a/lib/Frontend/CompilerInstance.cpp +++ b/lib/Frontend/CompilerInstance.cpp @@ -350,14 +350,18 @@ void CompilerInstance::createPreprocessor(TranslationUnitKind TUKind) { AttachDependencyGraphGen(*PP, DepOpts.DOTOutputFile, getHeaderSearchOpts().Sysroot); - for (auto &Listener : DependencyCollectors) - Listener->attachToPreprocessor(*PP); - // If we don't have a collector, but we are collecting module dependencies, // then we're the top level compiler instance and need to create one. - if (!ModuleDepCollector && !DepOpts.ModuleDependencyOutputDir.empty()) + if (!ModuleDepCollector && !DepOpts.ModuleDependencyOutputDir.empty()) { ModuleDepCollector = std::make_shared( DepOpts.ModuleDependencyOutputDir); + } + + if (ModuleDepCollector) + addDependencyCollector(ModuleDepCollector); + + for (auto &Listener : DependencyCollectors) + Listener->attachToPreprocessor(*PP); // Handle generating header include information, if requested. if (DepOpts.ShowHeaderIncludes) @@ -1309,8 +1313,6 @@ void CompilerInstance::createModuleManager() { if (TheDependencyFileGenerator) TheDependencyFileGenerator->AttachToASTReader(*ModuleManager); - if (ModuleDepCollector) - ModuleDepCollector->attachToASTReader(*ModuleManager); for (auto &Listener : DependencyCollectors) Listener->attachToASTReader(*ModuleManager); } diff --git a/lib/Frontend/ModuleDependencyCollector.cpp b/lib/Frontend/ModuleDependencyCollector.cpp index 1f187b12604..fc3958f601a 100644 --- a/lib/Frontend/ModuleDependencyCollector.cpp +++ b/lib/Frontend/ModuleDependencyCollector.cpp @@ -25,17 +25,16 @@ namespace { /// Private implementation for ModuleDependencyCollector class ModuleDependencyListener : public ASTReaderListener { ModuleDependencyCollector &Collector; - llvm::StringMap SymLinkMap; - - bool getRealPath(StringRef SrcPath, SmallVectorImpl &Result); - std::error_code copyToRoot(StringRef Src); public: ModuleDependencyListener(ModuleDependencyCollector &Collector) : Collector(Collector) {} bool needsInputFileVisitation() override { return true; } bool needsSystemInputFileVisitation() override { return true; } bool visitInputFile(StringRef Filename, bool IsSystem, bool IsOverridden, - bool IsExplicitModule) override; + bool IsExplicitModule) override { + Collector.addFile(Filename); + return true; + } }; } @@ -57,7 +56,7 @@ void ModuleDependencyCollector::writeFileMap() { std::error_code EC; llvm::raw_fd_ostream OS(Dest, EC, llvm::sys::fs::F_Text); if (EC) { - setHasErrors(); + HasErrors = true; return; } VFSWriter.write(OS); @@ -81,8 +80,8 @@ static bool real_path(StringRef SrcPath, SmallVectorImpl &RealPath) { #endif } -bool ModuleDependencyListener::getRealPath(StringRef SrcPath, - SmallVectorImpl &Result) { +bool ModuleDependencyCollector::getRealPath(StringRef SrcPath, + SmallVectorImpl &Result) { using namespace llvm::sys; SmallString<256> RealPath; StringRef FileName = path::filename(SrcPath); @@ -105,7 +104,7 @@ bool ModuleDependencyListener::getRealPath(StringRef SrcPath, return true; } -std::error_code ModuleDependencyListener::copyToRoot(StringRef Src) { +std::error_code ModuleDependencyCollector::copyToRoot(StringRef Src) { using namespace llvm::sys; // We need an absolute path to append to the root. @@ -131,7 +130,7 @@ std::error_code ModuleDependencyListener::copyToRoot(StringRef Src) { !StringRef(CanonicalPath).equals(RealPath); // Build the destination path. - SmallString<256> Dest = Collector.getDest(); + SmallString<256> Dest = getDest(); path::append(Dest, path::relative_path(HasRemovedSymlinkComponent ? RealPath : CanonicalPath)); @@ -145,18 +144,15 @@ std::error_code ModuleDependencyListener::copyToRoot(StringRef Src) { // Use the canonical path under the root for the file mapping. Also create // an additional entry for the real path. - Collector.addFileMapping(CanonicalPath, Dest); + addFileMapping(CanonicalPath, Dest); if (HasRemovedSymlinkComponent) - Collector.addFileMapping(RealPath, Dest); + addFileMapping(RealPath, Dest); return std::error_code(); } -bool ModuleDependencyListener::visitInputFile(StringRef Filename, bool IsSystem, - bool IsOverridden, - bool IsExplicitModule) { - if (Collector.insertSeen(Filename)) +void ModuleDependencyCollector::addFile(StringRef Filename) { + if (insertSeen(Filename)) if (copyToRoot(Filename)) - Collector.setHasErrors(); - return true; + HasErrors = true; } From 734325f21d14279382e402a7b11aec83408e3ef2 Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes Date: Wed, 30 Mar 2016 23:54:00 +0000 Subject: [PATCH 486/742] [VFS] Handle empty entries in directory traversal The VFS YAML files contain empty directory entries to describe that it's returning from a subdirectory before describing new files in the parent. In the future, we should properly sort and write YAML files avoiding such empty dirs and mitigate the extra recurson cost. However, since this is used by previous existing YAMLs, make the traversal work in their presence. rdar://problem/24499339 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@264970 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit bbf584f3c7c2c9c83fbb09d8c116a9048e729a6d) --- lib/Basic/VirtualFileSystem.cpp | 21 ++++---- .../crash-recovery/usr/include/module.map | 10 ++++ .../crash-recovery/usr/include/pthread.h | 1 + .../usr/include/pthread/pthread_impl.h | 1 + .../crash-recovery/usr/include/pthread_impl.h | 1 + .../Modules/crash-vfs-path-emptydir-entries.m | 48 +++++++++++++++++++ 6 files changed, 74 insertions(+), 8 deletions(-) create mode 100644 test/Modules/Inputs/crash-recovery/usr/include/pthread.h create mode 100644 test/Modules/Inputs/crash-recovery/usr/include/pthread/pthread_impl.h create mode 100644 test/Modules/Inputs/crash-recovery/usr/include/pthread_impl.h create mode 100644 test/Modules/crash-vfs-path-emptydir-entries.m diff --git a/lib/Basic/VirtualFileSystem.cpp b/lib/Basic/VirtualFileSystem.cpp index 95f6dc82468..81fd3ac8a8e 100644 --- a/lib/Basic/VirtualFileSystem.cpp +++ b/lib/Basic/VirtualFileSystem.cpp @@ -1368,16 +1368,21 @@ RedirectingFileSystem::lookupPath(sys::path::const_iterator Start, ++Start; #endif - if (CaseSensitive ? !Start->equals(From->getName()) - : !Start->equals_lower(From->getName())) - // failure to match - return make_error_code(llvm::errc::no_such_file_or_directory); + StringRef FromName = From->getName(); - ++Start; + // Forward the search to the next component in case this is an empty one. + if (!FromName.empty()) { + if (CaseSensitive ? !Start->equals(FromName) + : !Start->equals_lower(FromName)) + // failure to match + return make_error_code(llvm::errc::no_such_file_or_directory); - if (Start == End) { - // Match! - return From; + ++Start; + + if (Start == End) { + // Match! + return From; + } } auto *DE = dyn_cast(From); diff --git a/test/Modules/Inputs/crash-recovery/usr/include/module.map b/test/Modules/Inputs/crash-recovery/usr/include/module.map index 70aae494eff..9b429160db5 100644 --- a/test/Modules/Inputs/crash-recovery/usr/include/module.map +++ b/test/Modules/Inputs/crash-recovery/usr/include/module.map @@ -3,4 +3,14 @@ module cstd [system] { module stdio { header "stdio.h" } + + module pthread { + header "pthread.h" + export * + + module impl { + header "pthread_impl.h" + export * + } + } } diff --git a/test/Modules/Inputs/crash-recovery/usr/include/pthread.h b/test/Modules/Inputs/crash-recovery/usr/include/pthread.h new file mode 100644 index 00000000000..44ac9fce952 --- /dev/null +++ b/test/Modules/Inputs/crash-recovery/usr/include/pthread.h @@ -0,0 +1 @@ +#include "pthread/pthread_impl.h" diff --git a/test/Modules/Inputs/crash-recovery/usr/include/pthread/pthread_impl.h b/test/Modules/Inputs/crash-recovery/usr/include/pthread/pthread_impl.h new file mode 100644 index 00000000000..21a720a7c3c --- /dev/null +++ b/test/Modules/Inputs/crash-recovery/usr/include/pthread/pthread_impl.h @@ -0,0 +1 @@ +#define _PTHREAD_MUTEX_SIG_init 0x32AAABA7 diff --git a/test/Modules/Inputs/crash-recovery/usr/include/pthread_impl.h b/test/Modules/Inputs/crash-recovery/usr/include/pthread_impl.h new file mode 100644 index 00000000000..21a720a7c3c --- /dev/null +++ b/test/Modules/Inputs/crash-recovery/usr/include/pthread_impl.h @@ -0,0 +1 @@ +#define _PTHREAD_MUTEX_SIG_init 0x32AAABA7 diff --git a/test/Modules/crash-vfs-path-emptydir-entries.m b/test/Modules/crash-vfs-path-emptydir-entries.m new file mode 100644 index 00000000000..444c4556b51 --- /dev/null +++ b/test/Modules/crash-vfs-path-emptydir-entries.m @@ -0,0 +1,48 @@ +// REQUIRES: crash-recovery, shell + +// FIXME: This XFAIL is cargo-culted from crash-report.c. Do we need it? +// XFAIL: mingw32 + +// Test clang can collect symbolic link headers used in modules. +// crash reproducer if there's a symbolic link header file used in a module. + +// RUN: rm -rf %t +// RUN: mkdir -p %t/i %t/m %t %t/sysroot +// RUN: cp -a %S/Inputs/crash-recovery/usr %t/i/ + +// RUN: not env FORCE_CLANG_DIAGNOSTICS_CRASH= TMPDIR=%t TEMP=%t TMP=%t \ +// RUN: %clang -fsyntax-only %s -I %/t/i -isysroot %/t/sysroot/ \ +// RUN: -fmodules -fmodules-cache-path=%t/m/ 2>&1 | FileCheck %s + +// RUN: FileCheck --check-prefix=CHECKSRC %s -input-file %t/crash-vfs-*.m +// RUN: FileCheck --check-prefix=CHECKSH %s -input-file %t/crash-vfs-*.sh +// RUN: FileCheck --check-prefix=CHECKYAML %s -input-file \ +// RUN: %t/crash-vfs-*.cache/vfs/vfs.yaml +// RUN: find %t/crash-vfs-*.cache/vfs | \ +// RUN: grep "usr/include/stdio.h" | count 1 + +#include "usr/include/stdio.h" + +// CHECK: Preprocessed source(s) and associated run script(s) are located at: +// CHECK-NEXT: note: diagnostic msg: {{.*}}.m +// CHECK-NEXT: note: diagnostic msg: {{.*}}.cache + +// CHECKSRC: @import cstd.stdio; + +// CHECKSH: # Crash reproducer +// CHECKSH-NEXT: # Driver args: "-fsyntax-only" +// CHECKSH-NEXT: # Original command: {{.*$}} +// CHECKSH-NEXT: "-cc1" +// CHECKSH: "-isysroot" "{{[^"]*}}/sysroot/" +// CHECKSH-NOT: "-fmodules-cache-path=" +// CHECKSH: "crash-vfs-{{[^ ]*}}.m" +// CHECKSH: "-ivfsoverlay" "crash-vfs-{{[^ ]*}}.cache/vfs/vfs.yaml" + +// CHECKYAML: 'type': 'directory', +// CHECKYAML: 'name': "", +// CHECKYAML-NEXT: 'contents': [ +// CHECKYAML-NEXT: { +// CHECKYAML-NEXT: 'type': 'file', +// CHECKYAML-NEXT: 'name': "pthread_impl.h", +// CHECKYAML-NEXT: 'external-contents': "/{{.*}}/i/usr/include/pthread_impl.h" +// CHECKYAML-NEXT: }, From fac8539939efcaa2747e1493bed373fbcf984a93 Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes Date: Wed, 30 Mar 2016 23:54:25 +0000 Subject: [PATCH 487/742] [CrashReproducer] Add a module map callback for added headers The current ModuleDependencyCollector has a AST listener to collect header files present in loaded modules, but this isn't enough to collect all headers needed in the crash reproducer. One of the reasons is that the AST writer doesn't write symbolic link header paths in the pcm modules, this makes the listeners on the reader only able to collect the real files. Since the module maps could contain submodules that use headers which are symbolic links, not collecting those forbid the reproducer scripts to regen the modules. For instance: usr/include/module.map: ... module pthread { header "pthread.h" export * module impl { header "pthread_impl.h" export * } } ... usr/include/pthread/pthread_impl.h usr/include/pthread_impl.h -> pthread/pthread_impl.h The AST dump for the module above: blob data = 'pthread_impl.h' blob data = '//usr/include/pthread/pthread_impl.h' Note that we don't have "usr/include/pthread_impl.h" which is requested by the module.map in case we want to reconstruct the module in the reproducer. The reason the original symbolic link path isn't used is because the headers are kept by name and requested through the FileManager, which unique files and returns the real path only. To fix that, add a callback to be invoked everytime a header is added while parsing module maps and hook that up to the module dependecy collector. This callback is only registered when generating the reproducer. Differential Revision: http://reviews.llvm.org/D18585 rdar://problem/24499339 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@264971 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit fd80727d5056ec2380282081f7dbbed4cbae81ac) --- include/clang/Frontend/Utils.h | 3 +- include/clang/Lex/ModuleMap.h | 5 ++ lib/Frontend/ModuleDependencyCollector.cpp | 21 +++++++- lib/Lex/ModuleMap.cpp | 4 ++ .../crash-vfs-path-symlink-topheader.m | 50 +++++++++++++++++++ 5 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 test/Modules/crash-vfs-path-symlink-topheader.m diff --git a/include/clang/Frontend/Utils.h b/include/clang/Frontend/Utils.h index 10767a617c9..933468d5d9d 100644 --- a/include/clang/Frontend/Utils.h +++ b/include/clang/Frontend/Utils.h @@ -77,7 +77,7 @@ void DoPrintPreprocessedInput(Preprocessor &PP, raw_ostream* OS, /// interface. class DependencyCollector { public: - void attachToPreprocessor(Preprocessor &PP); + virtual void attachToPreprocessor(Preprocessor &PP); virtual void attachToASTReader(ASTReader &R); llvm::ArrayRef getDependencies() const { return Dependencies; } @@ -136,6 +136,7 @@ class ModuleDependencyCollector : public DependencyCollector { VFSWriter.addFileMapping(VPath, RPath); } + void attachToPreprocessor(Preprocessor &PP) override; void attachToASTReader(ASTReader &R) override; void writeFileMap(); diff --git a/include/clang/Lex/ModuleMap.h b/include/clang/Lex/ModuleMap.h index dfca6ed7881..4d7d4436ac6 100644 --- a/include/clang/Lex/ModuleMap.h +++ b/include/clang/Lex/ModuleMap.h @@ -50,6 +50,11 @@ class ModuleMapCallbacks { /// \param IsSystem Whether this is a module map from a system include path. virtual void moduleMapFileRead(SourceLocation FileStart, const FileEntry &File, bool IsSystem) {} + + /// \brief Called when a header is added during module map parsing. + /// + /// \param File The header file itself. + virtual void moduleMapAddHeader(const FileEntry &File) {} }; class ModuleMap { diff --git a/lib/Frontend/ModuleDependencyCollector.cpp b/lib/Frontend/ModuleDependencyCollector.cpp index fc3958f601a..e0ec674a16c 100644 --- a/lib/Frontend/ModuleDependencyCollector.cpp +++ b/lib/Frontend/ModuleDependencyCollector.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/Frontend/Utils.h" +#include "clang/Lex/Preprocessor.h" #include "clang/Serialization/ASTReader.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/iterator_range.h" @@ -22,7 +23,7 @@ using namespace clang; namespace { -/// Private implementation for ModuleDependencyCollector +/// Private implementations for ModuleDependencyCollector class ModuleDependencyListener : public ASTReaderListener { ModuleDependencyCollector &Collector; public: @@ -36,12 +37,30 @@ class ModuleDependencyListener : public ASTReaderListener { return true; } }; + +struct ModuleDependencyMMCallbacks : public ModuleMapCallbacks { + ModuleDependencyCollector &Collector; + ModuleDependencyMMCallbacks(ModuleDependencyCollector &Collector) + : Collector(Collector) {} + + void moduleMapAddHeader(const FileEntry &File) override { + StringRef HeaderPath = File.getName(); + if (llvm::sys::path::is_absolute(HeaderPath)) + Collector.addFile(HeaderPath); + } +}; + } void ModuleDependencyCollector::attachToASTReader(ASTReader &R) { R.addListener(llvm::make_unique(*this)); } +void ModuleDependencyCollector::attachToPreprocessor(Preprocessor &PP) { + PP.getHeaderSearchInfo().getModuleMap().addModuleMapCallbacks( + llvm::make_unique(*this)); +} + void ModuleDependencyCollector::writeFileMap() { if (Seen.empty()) return; diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp index 0ee8143c3b7..b07df67b2c1 100644 --- a/lib/Lex/ModuleMap.cpp +++ b/lib/Lex/ModuleMap.cpp @@ -831,6 +831,10 @@ void ModuleMap::addHeader(Module *Mod, Module::Header Header, HeaderInfo.MarkFileModuleHeader(Header.Entry, Role, isCompilingModuleHeader); } + + // Notify callbacks that we just added a new header. + for (const auto &Cb : Callbacks) + Cb->moduleMapAddHeader(*Header.Entry); } void ModuleMap::excludeHeader(Module *Mod, Module::Header Header) { diff --git a/test/Modules/crash-vfs-path-symlink-topheader.m b/test/Modules/crash-vfs-path-symlink-topheader.m new file mode 100644 index 00000000000..29f6b9322df --- /dev/null +++ b/test/Modules/crash-vfs-path-symlink-topheader.m @@ -0,0 +1,50 @@ +// REQUIRES: crash-recovery, shell + +// FIXME: This XFAIL is cargo-culted from crash-report.c. Do we need it? +// XFAIL: mingw32 + +// Test clang can collect symbolic link headers used in modules. +// crash reproducer if there's a symbolic link header file used in a module. + +// RUN: rm -rf %t +// RUN: mkdir -p %t/i %t/m %t %t/sysroot +// RUN: cp -a %S/Inputs/crash-recovery/usr %t/i/ +// RUN: rm -f %t/i/usr/include/pthread_impl.h +// RUN: ln -s pthread/pthread_impl.h %t/i/usr/include/pthread_impl.h + +// RUN: not env FORCE_CLANG_DIAGNOSTICS_CRASH= TMPDIR=%t TEMP=%t TMP=%t \ +// RUN: %clang -fsyntax-only %s -I %/t/i -isysroot %/t/sysroot/ \ +// RUN: -fmodules -fmodules-cache-path=%t/m/ 2>&1 | FileCheck %s + +// RUN: FileCheck --check-prefix=CHECKSRC %s -input-file %t/crash-vfs-*.m +// RUN: FileCheck --check-prefix=CHECKSH %s -input-file %t/crash-vfs-*.sh +// RUN: FileCheck --check-prefix=CHECKYAML %s -input-file \ +// RUN: %t/crash-vfs-*.cache/vfs/vfs.yaml +// RUN: find %t/crash-vfs-*.cache/vfs | \ +// RUN: grep "usr/include/pthread_impl.h" | count 1 + +#include "usr/include/stdio.h" + +// CHECK: Preprocessed source(s) and associated run script(s) are located at: +// CHECK-NEXT: note: diagnostic msg: {{.*}}.m +// CHECK-NEXT: note: diagnostic msg: {{.*}}.cache + +// CHECKSRC: @import cstd.stdio; + +// CHECKSH: # Crash reproducer +// CHECKSH-NEXT: # Driver args: "-fsyntax-only" +// CHECKSH-NEXT: # Original command: {{.*$}} +// CHECKSH-NEXT: "-cc1" +// CHECKSH: "-isysroot" "{{[^"]*}}/sysroot/" +// CHECKSH-NOT: "-fmodules-cache-path=" +// CHECKSH: "crash-vfs-{{[^ ]*}}.m" +// CHECKSH: "-ivfsoverlay" "crash-vfs-{{[^ ]*}}.cache/vfs/vfs.yaml" + +// CHECKYAML: 'type': 'directory', +// CHECKYAML: 'name': "", +// CHECKYAML-NEXT: 'contents': [ +// CHECKYAML-NEXT: { +// CHECKYAML-NEXT: 'type': 'file', +// CHECKYAML-NEXT: 'name': "pthread_impl.h", +// CHECKYAML-NEXT: 'external-contents': "/{{.*}}/i/usr/include/pthread_impl.h" +// CHECKYAML-NEXT: }, From 1cec5f7b784f6fdd4ba6cca16bac48d5066b1a18 Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes Date: Fri, 1 Apr 2016 17:39:08 +0000 Subject: [PATCH 488/742] [CrashReproducer] Add -fmodule-cache-path to reproducer script The cc1 invocation in the reproducer script should contain a valid path in -fmodule-cache-path; for that reuse ".cache/module" dir we already use to dump the vfs and modules. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265162 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 882be36111f5d811e6719ae2147c9dd36a4fc1ae) --- lib/Driver/Job.cpp | 13 +++++++++++++ test/Modules/crash-vfs-path-emptydir-entries.m | 1 + test/Modules/crash-vfs-path-symlink-component.m | 1 + test/Modules/crash-vfs-path-symlink-topheader.m | 1 + test/Modules/crash-vfs-path-traversal.m | 1 + test/Modules/crash-vfs-relative-overlay.m | 1 + 6 files changed, 18 insertions(+) diff --git a/lib/Driver/Job.cpp b/lib/Driver/Job.cpp index 25ce1a82e35..e612bdb94a6 100644 --- a/lib/Driver/Job.cpp +++ b/lib/Driver/Job.cpp @@ -18,6 +18,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/Program.h" #include "llvm/Support/raw_ostream.h" #include @@ -195,6 +196,18 @@ void Command::Print(raw_ostream &OS, const char *Terminator, bool Quote, printArg(OS, "-ivfsoverlay", Quote); OS << ' '; printArg(OS, CrashInfo->VFSPath.str().c_str(), Quote); + + // Insert -fmodules-cache-path and use the relative module directory + // .cache/vfs/modules where we already dumped the modules. + SmallString<128> RelModCacheDir = llvm::sys::path::parent_path( + llvm::sys::path::parent_path(CrashInfo->VFSPath)); + llvm::sys::path::append(RelModCacheDir, "modules"); + + std::string ModCachePath = "-fmodules-cache-path="; + ModCachePath.append(RelModCacheDir.c_str()); + + OS << ' '; + printArg(OS, ModCachePath.c_str(), Quote); } if (ResponseFile != nullptr) { diff --git a/test/Modules/crash-vfs-path-emptydir-entries.m b/test/Modules/crash-vfs-path-emptydir-entries.m index 444c4556b51..01560984ff7 100644 --- a/test/Modules/crash-vfs-path-emptydir-entries.m +++ b/test/Modules/crash-vfs-path-emptydir-entries.m @@ -37,6 +37,7 @@ // CHECKSH-NOT: "-fmodules-cache-path=" // CHECKSH: "crash-vfs-{{[^ ]*}}.m" // CHECKSH: "-ivfsoverlay" "crash-vfs-{{[^ ]*}}.cache/vfs/vfs.yaml" +// CHECKSH: "-fmodules-cache-path=crash-vfs-{{[^ ]*}}.cache/modules" // CHECKYAML: 'type': 'directory', // CHECKYAML: 'name': "", diff --git a/test/Modules/crash-vfs-path-symlink-component.m b/test/Modules/crash-vfs-path-symlink-component.m index 9687cc9a15f..674f80ea7f1 100644 --- a/test/Modules/crash-vfs-path-symlink-component.m +++ b/test/Modules/crash-vfs-path-symlink-component.m @@ -38,6 +38,7 @@ // CHECKSH-NOT: "-fmodules-cache-path=" // CHECKSH: "crash-vfs-{{[^ ]*}}.m" // CHECKSH: "-ivfsoverlay" "crash-vfs-{{[^ ]*}}.cache/vfs/vfs.yaml" +// CHECKSH: "-fmodules-cache-path=crash-vfs-{{[^ ]*}}.cache/modules" // CHECKYAML: 'type': 'directory' // CHECKYAML: 'name': "/[[PATH:.*]]/i/usr/include", diff --git a/test/Modules/crash-vfs-path-symlink-topheader.m b/test/Modules/crash-vfs-path-symlink-topheader.m index 29f6b9322df..72b666a8127 100644 --- a/test/Modules/crash-vfs-path-symlink-topheader.m +++ b/test/Modules/crash-vfs-path-symlink-topheader.m @@ -39,6 +39,7 @@ // CHECKSH-NOT: "-fmodules-cache-path=" // CHECKSH: "crash-vfs-{{[^ ]*}}.m" // CHECKSH: "-ivfsoverlay" "crash-vfs-{{[^ ]*}}.cache/vfs/vfs.yaml" +// CHECKSH: "-fmodules-cache-path=crash-vfs-{{[^ ]*}}.cache/modules" // CHECKYAML: 'type': 'directory', // CHECKYAML: 'name': "", diff --git a/test/Modules/crash-vfs-path-traversal.m b/test/Modules/crash-vfs-path-traversal.m index 60e73d4fb90..822a0dc2068 100644 --- a/test/Modules/crash-vfs-path-traversal.m +++ b/test/Modules/crash-vfs-path-traversal.m @@ -35,6 +35,7 @@ // CHECKSH-NOT: "-fmodules-cache-path=" // CHECKSH: "crash-vfs-{{[^ ]*}}.m" // CHECKSH: "-ivfsoverlay" "crash-vfs-{{[^ ]*}}.cache/vfs/vfs.yaml" +// CHECKSH: "-fmodules-cache-path=crash-vfs-{{[^ ]*}}.cache/modules" // CHECKYAML: 'type': 'directory' // CHECKYAML: 'name': "/[[PATH:.*]]/Inputs/crash-recovery/usr/include", diff --git a/test/Modules/crash-vfs-relative-overlay.m b/test/Modules/crash-vfs-relative-overlay.m index d4e1e0d3dfa..d157b133925 100644 --- a/test/Modules/crash-vfs-relative-overlay.m +++ b/test/Modules/crash-vfs-relative-overlay.m @@ -33,6 +33,7 @@ // CHECKSH-NOT: "-fmodules-cache-path=" // CHECKSH: "crash-vfs-{{[^ ]*}}.m" // CHECKSH: "-ivfsoverlay" "crash-vfs-{{[^ ]*}}.cache/vfs/vfs.yaml" +// CHECKSH: "-fmodules-cache-path=crash-vfs-{{[^ ]*}}.cache/modules" // CHECKYAML: 'type': 'directory' // CHECKYAML: 'name': "/[[PATH:.*]]/Inputs/crash-recovery/usr/include", From b8419faf15efe61617d4a9705bae8038fd89a22e Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes Date: Mon, 4 Apr 2016 20:26:57 +0000 Subject: [PATCH 489/742] [CrashReproducer] Pass -I, -F and -resource-dir to the reproducer script when using modules/vfs The reproducer should use -I/-F/-resource-dir in the same way as the original command. The VFS already collects the right headers but without these flags the reproducer will fail to do the right thing. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265343 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 10c44d19de75795e5549647022b86b88122f4440) --- lib/Driver/Job.cpp | 8 ++++---- test/Modules/crash-vfs-relative-overlay.m | 11 ++++++----- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/Driver/Job.cpp b/lib/Driver/Job.cpp index e612bdb94a6..a16bc42cfef 100644 --- a/lib/Driver/Job.cpp +++ b/lib/Driver/Job.cpp @@ -41,17 +41,16 @@ static int skipArgs(const char *Flag, bool HaveCrashVFS) { // These flags are all of the form -Flag and are treated as two // arguments. Therefore, we need to skip the flag and the next argument. bool Res = llvm::StringSwitch(Flag) - .Cases("-I", "-MF", "-MT", "-MQ", true) + .Cases("-MF", "-MT", "-MQ", "-serialize-diagnostic-file", true) .Cases("-o", "-coverage-file", "-dependency-file", true) .Cases("-fdebug-compilation-dir", "-idirafter", true) .Cases("-include", "-include-pch", "-internal-isystem", true) .Cases("-internal-externc-isystem", "-iprefix", "-iwithprefix", true) .Cases("-iwithprefixbefore", "-isystem", "-iquote", true) - .Cases("-resource-dir", "-serialize-diagnostic-file", true) .Cases("-dwarf-debug-flags", "-ivfsoverlay", true) .Cases("-header-include-file", "-diagnostic-log-file", true) // Some include flags shouldn't be skipped if we have a crash VFS - .Case("-isysroot", !HaveCrashVFS) + .Cases("-isysroot", "-I", "-F", "-resource-dir", !HaveCrashVFS) .Default(false); // Match found. @@ -72,7 +71,8 @@ static int skipArgs(const char *Flag, bool HaveCrashVFS) { // These flags are treated as a single argument (e.g., -F

    ). StringRef FlagRef(Flag); - if (FlagRef.startswith("-F") || FlagRef.startswith("-I") || + if ((!HaveCrashVFS && + (FlagRef.startswith("-F") || FlagRef.startswith("-I"))) || FlagRef.startswith("-fmodules-cache-path=") || FlagRef.startswith("-fapinotes-cache-path=")) return 1; diff --git a/test/Modules/crash-vfs-relative-overlay.m b/test/Modules/crash-vfs-relative-overlay.m index d157b133925..29552d0bb34 100644 --- a/test/Modules/crash-vfs-relative-overlay.m +++ b/test/Modules/crash-vfs-relative-overlay.m @@ -7,8 +7,9 @@ // RUN: mkdir -p %t/i %t/m %t // RUN: not env FORCE_CLANG_DIAGNOSTICS_CRASH= TMPDIR=%t TEMP=%t TMP=%t \ -// RUN: %clang -fsyntax-only %s -I %S/Inputs/crash-recovery -isysroot %/t/i/ \ -// RUN: -fmodules -fmodules-cache-path=%t/m/ 2>&1 | FileCheck %s +// RUN: %clang -fsyntax-only -nostdinc %s \ +// RUN: -I %S/Inputs/crash-recovery/usr/include -isysroot %/t/i/ \ +// RUN: -fmodules -fmodules-cache-path=%t/m/ 2>&1 | FileCheck %s // RUN: FileCheck --check-prefix=CHECKSRC %s -input-file %t/crash-vfs-*.m // RUN: FileCheck --check-prefix=CHECKSH %s -input-file %t/crash-vfs-*.sh @@ -17,7 +18,7 @@ // RUN: find %t/crash-vfs-*.cache/vfs | \ // RUN: grep "Inputs/crash-recovery/usr/include/stdio.h" | count 1 -#include "usr/include/stdio.h" +#include // CHECK: Preprocessed source(s) and associated run script(s) are located at: // CHECK-NEXT: note: diagnostic msg: {{.*}}.m @@ -29,8 +30,8 @@ // CHECKSH-NEXT: # Driver args: "-fsyntax-only" // CHECKSH-NEXT: # Original command: {{.*$}} // CHECKSH-NEXT: "-cc1" +// CHECKSH: "-resource-dir" // CHECKSH: "-isysroot" "{{[^"]*}}/i/" -// CHECKSH-NOT: "-fmodules-cache-path=" // CHECKSH: "crash-vfs-{{[^ ]*}}.m" // CHECKSH: "-ivfsoverlay" "crash-vfs-{{[^ ]*}}.cache/vfs/vfs.yaml" // CHECKSH: "-fmodules-cache-path=crash-vfs-{{[^ ]*}}.cache/modules" @@ -49,7 +50,7 @@ // the overlay dir is prefixed to access headers in .cache/vfs directory. // RUN: unset FORCE_CLANG_DIAGNOSTICS_CRASH -// RUN: %clang -E %s -I %S/Inputs/crash-recovery -isysroot %/t/i/ \ +// RUN: %clang -E %s -I %S/Inputs/crash-recovery/usr/include -isysroot %/t/i/ \ // RUN: -ivfsoverlay %t/crash-vfs-*.cache/vfs/vfs.yaml -fmodules \ // RUN: -fmodules-cache-path=%t/m/ 2>&1 \ // RUN: | FileCheck %s --check-prefix=CHECKOVERLAY From b4611a8011961ce63382c6aa529bfee1c87c7555 Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes Date: Thu, 7 Apr 2016 00:00:42 +0000 Subject: [PATCH 490/742] [CrashReproducer] Move ModuleDependencyCollector method around. NFC git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265621 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 6b2405c749a74d3db7ff500b313af57762c5a3ed) --- lib/Frontend/ModuleDependencyCollector.cpp | 36 +++++++++++----------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/Frontend/ModuleDependencyCollector.cpp b/lib/Frontend/ModuleDependencyCollector.cpp index e0ec674a16c..d9cf40af0f6 100644 --- a/lib/Frontend/ModuleDependencyCollector.cpp +++ b/lib/Frontend/ModuleDependencyCollector.cpp @@ -52,6 +52,24 @@ struct ModuleDependencyMMCallbacks : public ModuleMapCallbacks { } +// TODO: move this to Support/Path.h and check for HAVE_REALPATH? +static bool real_path(StringRef SrcPath, SmallVectorImpl &RealPath) { +#ifdef LLVM_ON_UNIX + char CanonicalPath[PATH_MAX]; + + // TODO: emit a warning in case this fails...? + if (!realpath(SrcPath.str().c_str(), CanonicalPath)) + return false; + + SmallString<256> RPath(CanonicalPath); + RealPath.swap(RPath); + return true; +#else + // FIXME: Add support for systems without realpath. + return false; +#endif +} + void ModuleDependencyCollector::attachToASTReader(ASTReader &R) { R.addListener(llvm::make_unique(*this)); } @@ -81,24 +99,6 @@ void ModuleDependencyCollector::writeFileMap() { VFSWriter.write(OS); } -// TODO: move this to Support/Path.h and check for HAVE_REALPATH? -static bool real_path(StringRef SrcPath, SmallVectorImpl &RealPath) { -#ifdef LLVM_ON_UNIX - char CanonicalPath[PATH_MAX]; - - // TODO: emit a warning in case this fails...? - if (!realpath(SrcPath.str().c_str(), CanonicalPath)) - return false; - - SmallString<256> RPath(CanonicalPath); - RealPath.swap(RPath); - return true; -#else - // FIXME: Add support for systems without realpath. - return false; -#endif -} - bool ModuleDependencyCollector::getRealPath(StringRef SrcPath, SmallVectorImpl &Result) { using namespace llvm::sys; From 5d1f02fa3d72bb8506eba7bbcea34591188813ec Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes Date: Thu, 7 Apr 2016 00:00:57 +0000 Subject: [PATCH 491/742] [CrashReproducer] Setup 'case-sensitive' in YAML files. The crash reproducer was not setting up case sensitivity in the VFS yaml files, which defaults to true. Make the crash reproducer explicitly set that flag based on the case sensitivity of the .cache path where vfs and modules are dumped. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265622 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 4dfb0b5b5f1c3585edcd72b3edf36850070abe8a) git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265630 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 80727827a6f7463a36c002d9384a795ac8f78ce3) git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265632 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit a916f7f5e9bbc096e7c6fab18be01b8013a39fe1) --- lib/Frontend/ModuleDependencyCollector.cpp | 32 +++++++++++++++++++--- test/Modules/crash-vfs-relative-overlay.m | 2 ++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/lib/Frontend/ModuleDependencyCollector.cpp b/lib/Frontend/ModuleDependencyCollector.cpp index d9cf40af0f6..3e6c0d24750 100644 --- a/lib/Frontend/ModuleDependencyCollector.cpp +++ b/lib/Frontend/ModuleDependencyCollector.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/Basic/CharInfo.h" #include "clang/Frontend/Utils.h" #include "clang/Lex/Preprocessor.h" #include "clang/Serialization/ASTReader.h" @@ -79,19 +80,42 @@ void ModuleDependencyCollector::attachToPreprocessor(Preprocessor &PP) { llvm::make_unique(*this)); } +static bool isCaseSensitivePath(StringRef Path) { + SmallString<256> TmpDest = Path, UpperDest, RealDest; + // Remove component traversals, links, etc. + if (!real_path(Path, TmpDest)) + return true; // Current default value in vfs.yaml + Path = TmpDest; + + // Change path to all upper case and ask for its real path, if the latter + // exists and is equal to Path, it's not case sensitive. Default to case + // sensitive in the absense of realpath, since this is what the VFSWriter + // already expects when sensitivity isn't setup. + for (auto &C : Path) + UpperDest.push_back(toUppercase(C)); + if (real_path(UpperDest, RealDest) && Path.equals(RealDest)) + return false; + return true; +} + void ModuleDependencyCollector::writeFileMap() { if (Seen.empty()) return; - SmallString<256> Dest = getDest(); - llvm::sys::path::append(Dest, "vfs.yaml"); + StringRef VFSDir = getDest(); // Default to use relative overlay directories in the VFS yaml file. This // allows crash reproducer scripts to work across machines. - VFSWriter.setOverlayDir(getDest()); + VFSWriter.setOverlayDir(VFSDir); + + // Explicitly set case sensitivity for the YAML writer. For that, find out + // the sensitivity at the path where the headers all collected to. + VFSWriter.setCaseSensitivity(isCaseSensitivePath(VFSDir)); std::error_code EC; - llvm::raw_fd_ostream OS(Dest, EC, llvm::sys::fs::F_Text); + SmallString<256> YAMLPath = VFSDir; + llvm::sys::path::append(YAMLPath, "vfs.yaml"); + llvm::raw_fd_ostream OS(YAMLPath, EC, llvm::sys::fs::F_Text); if (EC) { HasErrors = true; return; diff --git a/test/Modules/crash-vfs-relative-overlay.m b/test/Modules/crash-vfs-relative-overlay.m index 29552d0bb34..d785d09bd82 100644 --- a/test/Modules/crash-vfs-relative-overlay.m +++ b/test/Modules/crash-vfs-relative-overlay.m @@ -36,6 +36,8 @@ // CHECKSH: "-ivfsoverlay" "crash-vfs-{{[^ ]*}}.cache/vfs/vfs.yaml" // CHECKSH: "-fmodules-cache-path=crash-vfs-{{[^ ]*}}.cache/modules" +// CHECKYAML: 'case-sensitive': +// CHECKYAML-NEXT: 'overlay-relative': 'true', // CHECKYAML: 'type': 'directory' // CHECKYAML: 'name': "/[[PATH:.*]]/Inputs/crash-recovery/usr/include", // CHECKYAML-NEXT: 'contents': [ From 2bd8e9257edd08e9388d7cfedd7c6fd9c64f1e87 Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes Date: Wed, 13 Apr 2016 19:28:16 +0000 Subject: [PATCH 492/742] [VFS] Move default values to in-class member initialization. NFC git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@266233 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 3c75624a1661a906a8cb4de7056b0119f002c586) --- lib/Basic/VirtualFileSystem.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Basic/VirtualFileSystem.cpp b/lib/Basic/VirtualFileSystem.cpp index 81fd3ac8a8e..dd04cce392e 100644 --- a/lib/Basic/VirtualFileSystem.cpp +++ b/lib/Basic/VirtualFileSystem.cpp @@ -844,7 +844,7 @@ class RedirectingFileSystem : public vfs::FileSystem { /// \brief Whether to perform case-sensitive comparisons. /// /// Currently, case-insensitive matching only works correctly with ASCII. - bool CaseSensitive; + bool CaseSensitive = true; /// IsRelativeOverlay marks whether a IsExternalContentsPrefixDir path must /// be prefixed in every 'external-contents' when reading from YAML files. @@ -852,7 +852,7 @@ class RedirectingFileSystem : public vfs::FileSystem { /// \brief Whether to use to use the value of 'external-contents' for the /// names of files. This global value is overridable on a per-file basis. - bool UseExternalNames; + bool UseExternalNames = true; /// @} /// Virtual file paths and external files could be canonicalized without "..", @@ -869,7 +869,7 @@ class RedirectingFileSystem : public vfs::FileSystem { private: RedirectingFileSystem(IntrusiveRefCntPtr ExternalFS) - : ExternalFS(ExternalFS), CaseSensitive(true), UseExternalNames(true) {} + : ExternalFS(ExternalFS) {} /// \brief Looks up \p Path in \c Roots. ErrorOr lookupPath(const Twine &Path); From 77872a5e3f5592133fad0239601224c320235145 Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes Date: Wed, 13 Apr 2016 19:28:21 +0000 Subject: [PATCH 493/742] [CrashReproducer] Setup 'use-external-names' in YAML files. Hide the real paths when rebuilding from VFS by setting up the crash reproducer to use 'use-external-names' = false. This way we avoid module redifinition errors and consistently use the same paths against all modules. With this change on Darwin we are able to simulate a crash for a simple application using "Foundation/Foundation.h" (which relies on a bunch of different frameworks and headers) and successfully rebuild all the modules by relying solely at the VFS overlay. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@266234 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit acc90c3a8e86a9b1454ec76a86888171ba5d8bf4) --- include/clang/Basic/VirtualFileSystem.h | 4 ++++ lib/Basic/VirtualFileSystem.cpp | 13 +++++++++---- lib/Frontend/ModuleDependencyCollector.cpp | 4 ++++ test/Modules/crash-vfs-path-symlink-component.m | 5 ++++- test/Modules/crash-vfs-path-traversal.m | 5 ++++- test/Modules/crash-vfs-relative-overlay.m | 3 ++- 6 files changed, 27 insertions(+), 7 deletions(-) diff --git a/include/clang/Basic/VirtualFileSystem.h b/include/clang/Basic/VirtualFileSystem.h index cc13d72a5b4..9b95158987e 100644 --- a/include/clang/Basic/VirtualFileSystem.h +++ b/include/clang/Basic/VirtualFileSystem.h @@ -325,6 +325,7 @@ class YAMLVFSWriter { std::vector Mappings; Optional IsCaseSensitive; Optional IsOverlayRelative; + Optional UseExternalNames; std::string OverlayDir; public: @@ -333,6 +334,9 @@ class YAMLVFSWriter { void setCaseSensitivity(bool CaseSensitive) { IsCaseSensitive = CaseSensitive; } + void setUseExternalNames(bool UseExtNames) { + UseExternalNames = UseExtNames; + } void setOverlayDir(StringRef OverlayDirectory) { IsOverlayRelative = true; OverlayDir.assign(OverlayDirectory.str()); diff --git a/lib/Basic/VirtualFileSystem.cpp b/lib/Basic/VirtualFileSystem.cpp index dd04cce392e..d80e4193bad 100644 --- a/lib/Basic/VirtualFileSystem.cpp +++ b/lib/Basic/VirtualFileSystem.cpp @@ -1514,8 +1514,9 @@ class JSONWriter { public: JSONWriter(llvm::raw_ostream &OS) : OS(OS) {} - void write(ArrayRef Entries, Optional IsCaseSensitive, - Optional IsOverlayRelative, StringRef OverlayDir); + void write(ArrayRef Entries, Optional UseExternalNames, + Optional IsCaseSensitive, Optional IsOverlayRelative, + StringRef OverlayDir); }; } @@ -1568,6 +1569,7 @@ void JSONWriter::writeEntry(StringRef VPath, StringRef RPath) { } void JSONWriter::write(ArrayRef Entries, + Optional UseExternalNames, Optional IsCaseSensitive, Optional IsOverlayRelative, StringRef OverlayDir) { @@ -1578,6 +1580,9 @@ void JSONWriter::write(ArrayRef Entries, if (IsCaseSensitive.hasValue()) OS << " 'case-sensitive': '" << (IsCaseSensitive.getValue() ? "true" : "false") << "',\n"; + if (UseExternalNames.hasValue()) + OS << " 'use-external-names': '" + << (UseExternalNames.getValue() ? "true" : "false") << "',\n"; bool UseOverlayRelative = false; if (IsOverlayRelative.hasValue()) { UseOverlayRelative = IsOverlayRelative.getValue(); @@ -1639,8 +1644,8 @@ void YAMLVFSWriter::write(llvm::raw_ostream &OS) { return LHS.VPath < RHS.VPath; }); - JSONWriter(OS).write(Mappings, IsCaseSensitive, IsOverlayRelative, - OverlayDir); + JSONWriter(OS).write(Mappings, UseExternalNames, IsCaseSensitive, + IsOverlayRelative, OverlayDir); } VFSFromYamlDirIterImpl::VFSFromYamlDirIterImpl( diff --git a/lib/Frontend/ModuleDependencyCollector.cpp b/lib/Frontend/ModuleDependencyCollector.cpp index 3e6c0d24750..02d3c515ef0 100644 --- a/lib/Frontend/ModuleDependencyCollector.cpp +++ b/lib/Frontend/ModuleDependencyCollector.cpp @@ -112,6 +112,10 @@ void ModuleDependencyCollector::writeFileMap() { // the sensitivity at the path where the headers all collected to. VFSWriter.setCaseSensitivity(isCaseSensitivePath(VFSDir)); + // Do not rely on real path names when executing the crash reproducer scripts + // since we only want to actually use the files we have on the VFS cache. + VFSWriter.setUseExternalNames(false); + std::error_code EC; SmallString<256> YAMLPath = VFSDir; llvm::sys::path::append(YAMLPath, "vfs.yaml"); diff --git a/test/Modules/crash-vfs-path-symlink-component.m b/test/Modules/crash-vfs-path-symlink-component.m index 674f80ea7f1..04c56ac5f4a 100644 --- a/test/Modules/crash-vfs-path-symlink-component.m +++ b/test/Modules/crash-vfs-path-symlink-component.m @@ -40,6 +40,9 @@ // CHECKSH: "-ivfsoverlay" "crash-vfs-{{[^ ]*}}.cache/vfs/vfs.yaml" // CHECKSH: "-fmodules-cache-path=crash-vfs-{{[^ ]*}}.cache/modules" +// CHECKYAML: 'case-sensitive': +// CHECKYAML-NEXT: 'use-external-names': 'false', +// CHECKYAML-NEXT: 'overlay-relative': 'true', // CHECKYAML: 'type': 'directory' // CHECKYAML: 'name': "/[[PATH:.*]]/i/usr/include", // CHECKYAML-NEXT: 'contents': [ @@ -70,4 +73,4 @@ // RUN: -fmodules-cache-path=%t/m/ 2>&1 \ // RUN: | FileCheck %s --check-prefix=CHECKOVERLAY -// CHECKOVERLAY: @import cstd.stdio; /* clang -E: implicit import for "{{[^ ]*}}.cache/vfs/{{[^ ]*}}/i/usr/include/stdio.h" +// CHECKOVERLAY: @import cstd.stdio; /* clang -E: implicit import for "/{{[^ ].*}}/i/usr/x/../stdio.h" */ diff --git a/test/Modules/crash-vfs-path-traversal.m b/test/Modules/crash-vfs-path-traversal.m index 822a0dc2068..3377de47d0b 100644 --- a/test/Modules/crash-vfs-path-traversal.m +++ b/test/Modules/crash-vfs-path-traversal.m @@ -37,6 +37,9 @@ // CHECKSH: "-ivfsoverlay" "crash-vfs-{{[^ ]*}}.cache/vfs/vfs.yaml" // CHECKSH: "-fmodules-cache-path=crash-vfs-{{[^ ]*}}.cache/modules" +// CHECKYAML: 'case-sensitive': +// CHECKYAML-NEXT: 'use-external-names': 'false', +// CHECKYAML-NEXT: 'overlay-relative': 'true', // CHECKYAML: 'type': 'directory' // CHECKYAML: 'name': "/[[PATH:.*]]/Inputs/crash-recovery/usr/include", // CHECKYAML-NEXT: 'contents': [ @@ -59,4 +62,4 @@ // RUN: -fmodules-cache-path=%t/m/ 2>&1 \ // RUN: | FileCheck %s --check-prefix=CHECKOVERLAY -// CHECKOVERLAY: @import cstd.stdio; /* clang -E: implicit import for "{{[^ ]*}}.cache/vfs/{{[^ ]*}}/usr/include/stdio.h" +// CHECKOVERLAY: @import cstd.stdio; /* clang -E: implicit import for "/{{[^ ].*}}/usr/././//////include/../include/./././../include/stdio.h" */ diff --git a/test/Modules/crash-vfs-relative-overlay.m b/test/Modules/crash-vfs-relative-overlay.m index d785d09bd82..870987c58ab 100644 --- a/test/Modules/crash-vfs-relative-overlay.m +++ b/test/Modules/crash-vfs-relative-overlay.m @@ -37,6 +37,7 @@ // CHECKSH: "-fmodules-cache-path=crash-vfs-{{[^ ]*}}.cache/modules" // CHECKYAML: 'case-sensitive': +// CHECKYAML-NEXT: 'use-external-names': 'false', // CHECKYAML-NEXT: 'overlay-relative': 'true', // CHECKYAML: 'type': 'directory' // CHECKYAML: 'name': "/[[PATH:.*]]/Inputs/crash-recovery/usr/include", @@ -57,4 +58,4 @@ // RUN: -fmodules-cache-path=%t/m/ 2>&1 \ // RUN: | FileCheck %s --check-prefix=CHECKOVERLAY -// CHECKOVERLAY: @import cstd.stdio; /* clang -E: implicit import for "{{[^ ]*}}.cache/vfs/{{[^ ]*}}/usr/include/stdio.h" +// CHECKOVERLAY: @import cstd.stdio; /* clang -E: implicit import for "/{{[^ ].*}}/usr/include/stdio.h" */ From 815ace25e3657eb611631e7572676c2fcecf862f Mon Sep 17 00:00:00 2001 From: Bruno Cardoso Lopes Date: Wed, 13 Apr 2016 19:28:24 +0000 Subject: [PATCH 494/742] [CrashReproducer] Add test to run the reproducer script + modules Now that we have basic support in place, make sure the reproducer script works with modules for a simple testcase. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@266235 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 76ace1ff8d66981fcf542fb62e0539b203111479) --- test/Modules/crash-vfs-run-reproducer.m | 57 +++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 test/Modules/crash-vfs-run-reproducer.m diff --git a/test/Modules/crash-vfs-run-reproducer.m b/test/Modules/crash-vfs-run-reproducer.m new file mode 100644 index 00000000000..d0eaa931c97 --- /dev/null +++ b/test/Modules/crash-vfs-run-reproducer.m @@ -0,0 +1,57 @@ +// REQUIRES: crash-recovery, shell, system-darwin + +// RUN: rm -rf %t +// RUN: mkdir -p %t/i %t/m %t + +// RUN: not env FORCE_CLANG_DIAGNOSTICS_CRASH= TMPDIR=%t TEMP=%t TMP=%t \ +// RUN: %clang -fsyntax-only -nostdinc %s \ +// RUN: -I %S/Inputs/crash-recovery/usr/include -isysroot %/t/i/ \ +// RUN: -fmodules -fmodules-cache-path=%t/m/ 2>&1 | FileCheck %s + +// RUN: FileCheck --check-prefix=CHECKSRC %s -input-file %t/crash-vfs-*.m +// RUN: FileCheck --check-prefix=CHECKSH %s -input-file %t/crash-vfs-*.sh +// RUN: FileCheck --check-prefix=CHECKYAML %s -input-file \ +// RUN: %t/crash-vfs-*.cache/vfs/vfs.yaml +// RUN: find %t/crash-vfs-*.cache/vfs | \ +// RUN: grep "Inputs/crash-recovery/usr/include/stdio.h" | count 1 + +#include + +// CHECK: Preprocessed source(s) and associated run script(s) are located at: +// CHECK-NEXT: note: diagnostic msg: {{.*}}.m +// CHECK-NEXT: note: diagnostic msg: {{.*}}.cache + +// CHECKSRC: @import cstd.stdio; + +// CHECKSH: # Crash reproducer +// CHECKSH-NEXT: # Driver args: "-fsyntax-only" +// CHECKSH-NEXT: # Original command: {{.*$}} +// CHECKSH-NEXT: "-cc1" +// CHECKSH: "-resource-dir" +// CHECKSH: "-isysroot" "{{[^"]*}}/i/" +// CHECKSH: "crash-vfs-{{[^ ]*}}.m" +// CHECKSH: "-ivfsoverlay" "crash-vfs-{{[^ ]*}}.cache/vfs/vfs.yaml" +// CHECKSH: "-fmodules-cache-path=crash-vfs-{{[^ ]*}}.cache/modules" + +// CHECKYAML: 'case-sensitive': +// CHECKYAML-NEXT: 'use-external-names': 'false', +// CHECKYAML-NEXT: 'overlay-relative': 'true', +// CHECKYAML: 'type': 'directory' +// CHECKYAML: 'name': "/[[PATH:.*]]/Inputs/crash-recovery/usr/include", +// CHECKYAML-NEXT: 'contents': [ +// CHECKYAML-NEXT: { +// CHECKYAML-NEXT: 'type': 'file', +// CHECKYAML-NEXT: 'name': "module.map", +// CHECKYAML-NOT: 'external-contents': "{{[^ ]*}}.cache +// CHECKYAML-NEXT: 'external-contents': "/[[PATH]]/Inputs/crash-recovery/usr/include/module.map" +// CHECKYAML-NEXT: }, + +// Run the reproducer script - regular exit code is enough to test it works. +// Note that we don't yet support reusing the modules pcm; what we do +// support is re-building the modules relying solely on the header files dumped +// inside .cache/vfs, mapped by .cache/vfs/vfs.yaml. + +// RUN: cd %t +// RUN: rm -rf crash-vfs-run-reproducer-*.cache/modules/* +// RUN: chmod 755 crash-vfs-*.sh +// RUN: ./crash-vfs-*.sh From 4e08a66bfc686fda3be5f6f584107614b04ca5c0 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Tue, 12 Apr 2016 19:29:52 +0000 Subject: [PATCH 495/742] [analyzer] Nullability: Suppress return diagnostics in inlined functions. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The nullability checker can sometimes miss detecting nullability precondition violations in inlined functions because the binding for the parameter that violated the precondition becomes dead before the return: int * _Nonnull callee(int * _Nonnull p2) { if (!p2) // p2 becomes dead here, so binding removed. return 0; // warning here because value stored in p2 is symbolic. else return p2; } int *caller(int * _Nonnull p1) { return callee(p1); } The fix, which is quite blunt, is to not warn about null returns in inlined methods/functions. This won’t lose much coverage for ObjC because the analyzer always analyzes each ObjC method at the top level in addition to inlined. It *will* lose coverage for C — but there aren’t that many codebases with C nullability annotations. rdar://problem/25615050 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@266109 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 9b05ffd15d976bbd74b90933c3d98bd91b338d28) --- .../Checkers/NullabilityChecker.cpp | 3 +- test/Analysis/nullability.mm | 35 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp index 35620d33acf..d8a224ea2f4 100644 --- a/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp @@ -562,7 +562,8 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S, if (Filter.CheckNullReturnedFromNonnull && NullReturnedFromNonNull && RetExprTypeLevelNullability != Nullability::Nonnull && - !InSuppressedMethodFamily) { + !InSuppressedMethodFamily && + C.getLocationContext()->inTopFrame()) { static CheckerProgramPointTag Tag(this, "NullReturnedFromNonnull"); ExplodedNode *N = C.generateErrorNode(State, &Tag); if (!N) diff --git a/test/Analysis/nullability.mm b/test/Analysis/nullability.mm index 0a3ae7a1968..1e01cdf4ca6 100644 --- a/test/Analysis/nullability.mm +++ b/test/Analysis/nullability.mm @@ -238,6 +238,41 @@ void testPreconditionViolationInInlinedFunction(Dummy *p) { doNotWarnWhenPreconditionIsViolated(p); } +@interface TestInlinedPreconditionViolationClass : NSObject +@end + +@implementation TestInlinedPreconditionViolationClass +-(Dummy * _Nonnull) calleeWithParam:(Dummy * _Nonnull) p2 { + Dummy *x = 0; + if (!p2) // p2 binding becomes dead at this point. + return x; // no-warning + else + return p2; +} + +-(Dummy *)callerWithParam:(Dummy * _Nonnull) p1 { + return [self calleeWithParam:p1]; +} + +@end + +int * _Nonnull InlinedPreconditionViolationInFunctionCallee(int * _Nonnull p2) { + int *x = 0; + if (!p2) // p2 binding becomes dead at this point. + return x; // no-warning + else + return p2; +} + +int * _Nonnull InlinedReturnNullOverSuppressionCallee(int * _Nonnull p2) { + int *result = 0; + return result; // no-warning; but this is an over suppression +} + +int *InlinedReturnNullOverSuppressionCaller(int * _Nonnull p1) { + return InlinedReturnNullOverSuppressionCallee(p1); +} + void inlinedNullable(Dummy *_Nullable p) { if (p) return; } From 2a0548a0a48d1af61d6f7af4743c51fc7f109879 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Wed, 13 Apr 2016 00:41:54 +0000 Subject: [PATCH 496/742] [analyzer] Nullability: Treat nil _Nonnull ivar as invariant violation. Treat a _Nonnull ivar that is nil as an invariant violation in a similar fashion to how a nil _Nonnull parameter is treated as a precondition violation. This avoids warning on defensive returns of nil on defensive internal checks, such as the following common idiom: @class InternalImplementation @interface PublicClass { InternalImplementation * _Nonnull _internal; } -(id _Nonnull)foo; @end @implementation PublicClass -(id _Nonnull)foo { if (!_internal) return nil; // no-warning return [_internal foo]; } @end rdar://problem/24485171 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@266157 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 215d5ab34a51bfb5f8bfe13b1c7800ae01462e63) --- .../Checkers/NullabilityChecker.cpp | 70 +++++++++++++++---- test/Analysis/nullability.mm | 42 +++++++++++ 2 files changed, 98 insertions(+), 14 deletions(-) diff --git a/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp index d8a224ea2f4..0941b5240e9 100644 --- a/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp @@ -356,29 +356,70 @@ static Nullability getNullabilityAnnotation(QualType Type) { return Nullability::Unspecified; } -template +/// Returns true when the value stored at the given location is null +/// and the passed in type is nonnnull. +static bool checkValueAtLValForInvariantViolation(ProgramStateRef State, + SVal LV, QualType T) { + if (getNullabilityAnnotation(T) != Nullability::Nonnull) + return false; + + auto RegionVal = LV.getAs(); + if (!RegionVal) + return false; + + auto StoredVal = + State->getSVal(RegionVal->getRegion()).getAs(); + if (!StoredVal) + return false; + + if (getNullConstraint(*StoredVal, State) == NullConstraint::IsNull) + return true; + + return false; +} + static bool -checkParamsForPreconditionViolation(const ParamVarDeclRange &Params, +checkParamsForPreconditionViolation(ArrayRef Params, ProgramStateRef State, const LocationContext *LocCtxt) { for (const auto *ParamDecl : Params) { if (ParamDecl->isParameterPack()) break; - if (getNullabilityAnnotation(ParamDecl->getType()) != Nullability::Nonnull) - continue; + SVal LV = State->getLValue(ParamDecl, LocCtxt); + if (checkValueAtLValForInvariantViolation(State, LV, + ParamDecl->getType())) { + return true; + } + } + return false; +} - auto RegVal = State->getLValue(ParamDecl, LocCtxt) - .template getAs(); - if (!RegVal) - continue; +static bool +checkSelfIvarsForInvariantViolation(ProgramStateRef State, + const LocationContext *LocCtxt) { + auto *MD = dyn_cast(LocCtxt->getDecl()); + if (!MD || !MD->isInstanceMethod()) + return false; - auto ParamValue = State->getSVal(RegVal->getRegion()) - .template getAs(); - if (!ParamValue) - continue; + const ImplicitParamDecl *SelfDecl = LocCtxt->getSelfDecl(); + if (!SelfDecl) + return false; + + SVal SelfVal = State->getSVal(State->getRegion(SelfDecl, LocCtxt)); + + const ObjCObjectPointerType *SelfType = + dyn_cast(SelfDecl->getType()); + if (!SelfType) + return false; + + const ObjCInterfaceDecl *ID = SelfType->getInterfaceDecl(); + if (!ID) + return false; - if (getNullConstraint(*ParamValue, State) == NullConstraint::IsNull) { + for (const auto *IvarDecl : ID->ivars()) { + SVal LV = State->getLValue(IvarDecl, SelfVal); + if (checkValueAtLValForInvariantViolation(State, LV, IvarDecl->getType())) { return true; } } @@ -405,7 +446,8 @@ static bool checkInvariantViolation(ProgramStateRef State, ExplodedNode *N, else return false; - if (checkParamsForPreconditionViolation(Params, State, LocCtxt)) { + if (checkParamsForPreconditionViolation(Params, State, LocCtxt) || + checkSelfIvarsForInvariantViolation(State, LocCtxt)) { if (!N->isSink()) C.addTransition(State->set(true), N); return true; diff --git a/test/Analysis/nullability.mm b/test/Analysis/nullability.mm index 1e01cdf4ca6..d4deecd9b5b 100644 --- a/test/Analysis/nullability.mm +++ b/test/Analysis/nullability.mm @@ -444,3 +444,45 @@ void callMethodInSystemHeader() { // expected-warning@-2{{Nullable pointer is passed to a callee that requires a non-null 1st parameter}} #endif } + +// Test to make sure the analyzer doesn't warn when an a nullability invariant +// has already been found to be violated on an instance variable. + +@class MyInternalClass; +@interface MyClass : NSObject { + MyInternalClass * _Nonnull _internal; +} +@end + +@interface MyInternalClass : NSObject { + @public + id _someIvar; +} +-(id _Nonnull)methodWithInternalImplementation; +@end + +@interface MyClass () { + MyInternalClass * _Nonnull _nilledOutInternal; +} +@end + +@implementation MyClass +-(id _Nonnull)methodWithInternalImplementation { + if (!_internal) + return nil; // no-warning + + return [_internal methodWithInternalImplementation]; +} + +- (id _Nonnull)methodReturningIvarInImplementation; { + return _internal == 0 ? nil : _internal->_someIvar; // no-warning +} + +-(id _Nonnull)methodWithNilledOutInternal { + // The cast below should (but does not yet) suppress the warning on the + // assignment. + _nilledOutInternal = (id _Nonnull)nil; // expected-warning {{Null is assigned to a pointer which is expected to have non-null value}} + + return nil; // no-warning +} +@end From ea2749a023e828dbbadc82f1321d432936125522 Mon Sep 17 00:00:00 2001 From: Devin Coughlin Date: Wed, 13 Apr 2016 17:59:24 +0000 Subject: [PATCH 497/742] [analyzer] Nullability: Suppress diagnostic on bind with cast. Update the nullability checker to allow an explicit cast to nonnull to suppress a warning on an assignment of nil to a nonnull: id _Nonnull x = (id _Nonnull)nil; // no-warning This suppression as already possible for diagnostics on returns and function/method arguments. rdar://problem/25381178 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@266219 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 1eb408116bbfa3856e471f917c0901864607064b) --- .../Checkers/NullabilityChecker.cpp | 34 +++++++++++--- test/Analysis/nullability.mm | 45 +++++++++++++++++-- 2 files changed, 70 insertions(+), 9 deletions(-) diff --git a/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp index 0941b5240e9..d7ec6b10c6f 100644 --- a/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp @@ -1103,26 +1103,48 @@ void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S, ValNullability = getNullabilityAnnotation(Sym->getType()); Nullability LocNullability = getNullabilityAnnotation(LocType); + + // If the type of the RHS expression is nonnull, don't warn. This + // enables explicit suppression with a cast to nonnull. + Nullability ValueExprTypeLevelNullability = Nullability::Unspecified; + const Expr *ValueExpr = matchValueExprForBind(S); + if (ValueExpr) { + ValueExprTypeLevelNullability = + getNullabilityAnnotation(lookThroughImplicitCasts(ValueExpr)->getType()); + } + + bool NullAssignedToNonNull = (LocNullability == Nullability::Nonnull && + RhsNullness == NullConstraint::IsNull); if (Filter.CheckNullPassedToNonnull && - RhsNullness == NullConstraint::IsNull && + NullAssignedToNonNull && ValNullability != Nullability::Nonnull && - LocNullability == Nullability::Nonnull && + ValueExprTypeLevelNullability != Nullability::Nonnull && !isARCNilInitializedLocal(C, S)) { static CheckerProgramPointTag Tag(this, "NullPassedToNonnull"); ExplodedNode *N = C.generateErrorNode(State, &Tag); if (!N) return; - const Stmt *ValueExpr = matchValueExprForBind(S); - if (!ValueExpr) - ValueExpr = S; + + const Stmt *ValueStmt = S; + if (ValueExpr) + ValueStmt = ValueExpr; reportBugIfInvariantHolds("Null is assigned to a pointer which is " "expected to have non-null value", ErrorKind::NilAssignedToNonnull, N, nullptr, C, - ValueExpr); + ValueStmt); + return; + } + + // If null was returned from a non-null function, mark the nullability + // invariant as violated even if the diagnostic was suppressed. + if (NullAssignedToNonNull) { + State = State->set(true); + C.addTransition(State); return; } + // Intentionally missing case: '0' is bound to a reference. It is handled by // the DereferenceChecker. diff --git a/test/Analysis/nullability.mm b/test/Analysis/nullability.mm index d4deecd9b5b..c6d6519d90e 100644 --- a/test/Analysis/nullability.mm +++ b/test/Analysis/nullability.mm @@ -174,6 +174,47 @@ void testIndirectCastNilToNonnullAndPass() { takesNonnull(p); // expected-warning {{Null passed to a callee that requires a non-null 1st parameter}} } +void testDirectCastNilToNonnullAndAssignToLocalInInitializer() { + Dummy * _Nonnull nonnullLocalWithAssignmentInInitializer = (Dummy * _Nonnull)0; // no-warning + (void)nonnullLocalWithAssignmentInInitializer; + + // Since we've already had an invariant violation along this path, + // we shouldn't warn here. + nonnullLocalWithAssignmentInInitializer = 0; + (void)nonnullLocalWithAssignmentInInitializer; + +} + +void testDirectCastNilToNonnullAndAssignToLocal(Dummy * _Nonnull p) { + Dummy * _Nonnull nonnullLocalWithAssignment = p; + nonnullLocalWithAssignment = (Dummy * _Nonnull)0; // no-warning + (void)nonnullLocalWithAssignment; + + // Since we've already had an invariant violation along this path, + // we shouldn't warn here. + nonnullLocalWithAssignment = 0; + (void)nonnullLocalWithAssignment; +} + +void testDirectCastNilToNonnullAndAssignToParam(Dummy * _Nonnull p) { + p = (Dummy * _Nonnull)0; // no-warning +} + +@interface ClassWithNonnullIvar : NSObject { + Dummy *_nonnullIvar; +} +@end + +@implementation ClassWithNonnullIvar +-(void)testDirectCastNilToNonnullAndAssignToIvar { + _nonnullIvar = (Dummy * _Nonnull)0; // no-warning; + + // Since we've already had an invariant violation along this path, + // we shouldn't warn here. + _nonnullIvar = 0; +} +@end + void testIndirectNilPassToNonnull() { Dummy *p = 0; takesNonnull(p); // expected-warning {{Null passed to a callee that requires a non-null 1st parameter}} @@ -479,9 +520,7 @@ - (id _Nonnull)methodReturningIvarInImplementation; { } -(id _Nonnull)methodWithNilledOutInternal { - // The cast below should (but does not yet) suppress the warning on the - // assignment. - _nilledOutInternal = (id _Nonnull)nil; // expected-warning {{Null is assigned to a pointer which is expected to have non-null value}} + _nilledOutInternal = (id _Nonnull)nil; return nil; // no-warning } From 6ab42547b7390473f10d893d1c637e4291c405af Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Fri, 15 Apr 2016 15:55:45 +0000 Subject: [PATCH 498/742] Update to match LLVM changes for PR27284. (Reverse the ownership between DICompileUnit and DISubprogram.) http://reviews.llvm.org/D19034 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@266445 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 92e0abcf102d39387eef8758f9d204d1d4d5a218) --- lib/CodeGen/CGDebugInfo.cpp | 10 ++-- test/CodeGen/debug-info-scope-file.c | 2 +- test/CodeGen/debug-info.c | 8 +-- test/CodeGenCXX/PR20038.cpp | 2 +- .../CodeGenCXX/debug-info-anon-union-vars.cpp | 4 +- .../debug-info-function-context.cpp | 4 +- test/CodeGenCXX/debug-info-namespace.cpp | 14 +++-- .../debug-info-ptr-to-member-function.cpp | 10 ++-- .../CodeGenCXX/debug-info-template-member.cpp | 19 ++++--- test/CodeGenCXX/debug-info.cpp | 7 ++- test/CodeGenCXX/debug-lambda-expressions.cpp | 55 +++++++++---------- test/CodeGenCXX/debug-lambda-this.cpp | 4 +- .../CodeGenCXX/linetable-virtual-variadic.cpp | 4 -- test/CodeGenObjC/debug-info-block-type.m | 15 +++-- test/CodeGenObjC/debug-property-synth.m | 4 +- test/Modules/ExtDebugInfo.m | 15 ++--- test/Modules/ModuleDebugInfo.cpp | 6 +- test/Modules/ModuleDebugInfo.m | 31 +++++------ 18 files changed, 105 insertions(+), 109 deletions(-) diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index 348fa7c2f96..f475d127b6c 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -2799,11 +2799,11 @@ void CGDebugInfo::EmitFunctionDecl(GlobalDecl GD, SourceLocation Loc, unsigned LineNo = getLineNumber(Loc); unsigned ScopeLine = 0; - DBuilder.createFunction(FDContext, Name, LinkageName, Unit, LineNo, - getOrCreateFunctionType(D, FnType, Unit), - false /*internalLinkage*/, true /*definition*/, - ScopeLine, Flags, CGM.getLangOpts().Optimize, - TParamsArray.get(), getFunctionDeclaration(D)); + DBuilder.retainType(DBuilder.createFunction( + FDContext, Name, LinkageName, Unit, LineNo, + getOrCreateFunctionType(D, FnType, Unit), false /*internalLinkage*/, + false /*definition*/, ScopeLine, Flags, CGM.getLangOpts().Optimize, + TParamsArray.get(), getFunctionDeclaration(D))); } void CGDebugInfo::EmitLocation(CGBuilderTy &Builder, SourceLocation Loc) { diff --git a/test/CodeGen/debug-info-scope-file.c b/test/CodeGen/debug-info-scope-file.c index 296ec05826f..94123bbc493 100644 --- a/test/CodeGen/debug-info-scope-file.c +++ b/test/CodeGen/debug-info-scope-file.c @@ -6,8 +6,8 @@ // CHECK: ret void, !dbg [[F1_LINE:![0-9]*]] // CHECK: ret void, !dbg [[F2_LINE:![0-9]*]] // CHECK: [[F1:![0-9]*]] = distinct !DISubprogram(name: "f1",{{.*}} isDefinition: true -// CHECK: [[F2:![0-9]*]] = distinct !DISubprogram(name: "f2",{{.*}} isDefinition: true // CHECK: [[F1_LINE]] = !DILocation({{.*}}, scope: [[F1]]) +// CHECK: [[F2:![0-9]*]] = distinct !DISubprogram(name: "f2",{{.*}} isDefinition: true // CHECK: [[F2_LINE]] = !DILocation({{.*}}, scope: [[F2]]) void f1() { diff --git a/test/CodeGen/debug-info.c b/test/CodeGen/debug-info.c index d122e7fe5cc..f0215cc9232 100644 --- a/test/CodeGen/debug-info.c +++ b/test/CodeGen/debug-info.c @@ -7,7 +7,7 @@ void convert(void) { // PR2784 -struct OPAQUE; // CHECK: DW_TAG_structure_type +struct OPAQUE; // CHECK-DAG: DW_TAG_structure_type, name: "OPAQUE" typedef struct OPAQUE *PTR; PTR p; @@ -42,19 +42,19 @@ struct foo2 foo2; // Radar 7325611 -// CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "barfoo" +// CHECK-DAG: !DIDerivedType(tag: DW_TAG_typedef, name: "barfoo" typedef int barfoo; barfoo foo() { } -// CHECK: __uint128_t +// CHECK-DAG: __uint128_t __uint128_t foo128 () { __uint128_t int128 = 44; return int128; } -// CHECK: uint64x2_t +// CHECK-DAG: uint64x2_t typedef unsigned long long uint64_t; typedef uint64_t uint64x2_t __attribute__((ext_vector_type(2))); uint64x2_t extvectbar[4]; diff --git a/test/CodeGenCXX/PR20038.cpp b/test/CodeGenCXX/PR20038.cpp index 2d7043dcda3..095705f389b 100644 --- a/test/CodeGenCXX/PR20038.cpp +++ b/test/CodeGenCXX/PR20038.cpp @@ -7,8 +7,8 @@ extern bool b; // CHECK: call {{.*}}, !dbg [[DTOR_CALL1_LOC:![0-9]*]] // CHECK: call {{.*}}, !dbg [[DTOR_CALL2_LOC:![0-9]*]] // CHECK: [[FUN1:.*]] = distinct !DISubprogram(name: "fun1",{{.*}} isDefinition: true -// CHECK: [[FUN2:.*]] = distinct !DISubprogram(name: "fun2",{{.*}} isDefinition: true // CHECK: [[DTOR_CALL1_LOC]] = !DILocation(line: [[@LINE+1]], scope: [[FUN1]]) void fun1() { b && (C(), 1); } +// CHECK: [[FUN2:.*]] = distinct !DISubprogram(name: "fun2",{{.*}} isDefinition: true // CHECK: [[DTOR_CALL2_LOC]] = !DILocation(line: [[@LINE+1]], scope: [[FUN2]]) bool fun2() { return (C(), b) && 0; } diff --git a/test/CodeGenCXX/debug-info-anon-union-vars.cpp b/test/CodeGenCXX/debug-info-anon-union-vars.cpp index 5b0370eb749..80742250d84 100644 --- a/test/CodeGenCXX/debug-info-anon-union-vars.cpp +++ b/test/CodeGenCXX/debug-info-anon-union-vars.cpp @@ -44,8 +44,8 @@ void instantiate(int x) { buildBytes(x); } -// CHECK: [[FILE:.*]] = !DIFile(filename: "{{.*}}debug-info-anon-union-vars.cpp", -// CHECK: !DIGlobalVariable(name: "c",{{.*}} file: [[FILE]], line: 6,{{.*}} isLocal: true, isDefinition: true +// CHECK: !DIGlobalVariable(name: "c",{{.*}} file: [[FILE:.*]], line: 6,{{.*}} isLocal: true, isDefinition: true +// CHECK: [[FILE]] = !DIFile(filename: "{{.*}}debug-info-anon-union-vars.cpp", // CHECK: !DIGlobalVariable(name: "d",{{.*}} file: [[FILE]], line: 6,{{.*}} isLocal: true, isDefinition: true // CHECK: !DIGlobalVariable(name: "a",{{.*}} file: [[FILE]], line: 6,{{.*}} isLocal: true, isDefinition: true // CHECK: !DIGlobalVariable(name: "b",{{.*}} file: [[FILE]], line: 6,{{.*}} isLocal: true, isDefinition: true diff --git a/test/CodeGenCXX/debug-info-function-context.cpp b/test/CodeGenCXX/debug-info-function-context.cpp index 24f9f1bda63..47a8d4d6021 100644 --- a/test/CodeGenCXX/debug-info-function-context.cpp +++ b/test/CodeGenCXX/debug-info-function-context.cpp @@ -26,11 +26,11 @@ int global_namespace_variable = 1; // function has the file as a context. // CHECK: ![[FILE:[0-9]+]] = !DIFile(filename: "{{.*}}context.cpp", +// CHECK: ![[NS:.*]] = !DINamespace(name: "ns" // CHECK: !DISubprogram(name: "member_function",{{.*}} scope: !"_ZTS1C",{{.*}} isDefinition: true // CHECK: !DISubprogram(name: "static_member_function",{{.*}} scope: !"_ZTS1C",{{.*}} isDefinition: true // CHECK: !DISubprogram(name: "global_function",{{.*}} scope: ![[FILE]],{{.*}} isDefinition: true -// CHECK: !DISubprogram(name: "global_namespace_function",{{.*}} scope: ![[NS:[0-9]+]],{{.*}} isDefinition: true -// CHECK: ![[NS]] = !DINamespace(name: "ns" +// CHECK: !DISubprogram(name: "global_namespace_function",{{.*}} scope: ![[NS]],{{.*}} isDefinition: true diff --git a/test/CodeGenCXX/debug-info-namespace.cpp b/test/CodeGenCXX/debug-info-namespace.cpp index bebb24ab5d6..5cdf91bc1bf 100644 --- a/test/CodeGenCXX/debug-info-namespace.cpp +++ b/test/CodeGenCXX/debug-info-namespace.cpp @@ -67,10 +67,7 @@ void B::func_fwd() {} // CHECK: [[BAR:![0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "bar", // CHECK-SAME: line: 6 // CHECK-SAME: DIFlagFwdDecl -// CHECK: [[F1:![0-9]+]] = distinct !DISubprogram(name: "f1",{{.*}} line: 4 -// CHECK-SAME: isDefinition: true -// CHECK: [[FUNC:![0-9]+]] = distinct !DISubprogram(name: "func",{{.*}} isDefinition: true -// CHECK: [[FUNC_FWD:![0-9]+]] = distinct !DISubprogram(name: "func_fwd",{{.*}} line: 47,{{.*}} isDefinition: true + // CHECK: [[I:![0-9]+]] = !DIGlobalVariable(name: "i",{{.*}} scope: [[NS]], // CHECK: [[VAR_FWD:![0-9]+]] = !DIGlobalVariable(name: "var_fwd",{{.*}} scope: [[NS]], // CHECK-SAME: line: 44 @@ -82,11 +79,15 @@ void B::func_fwd() {} // CHECK: [[M3]] = !DIImportedEntity(tag: DW_TAG_imported_declaration, name: "E", scope: [[CU]], entity: [[CTXT]], line: 19) // CHECK: [[M4]] = !DIImportedEntity(tag: DW_TAG_imported_module, scope: [[LEX2:![0-9]+]], entity: [[NS]], line: 23) // CHECK: [[LEX2]] = distinct !DILexicalBlock(scope: [[LEX1:![0-9]+]], file: [[FOOCPP]], -// CHECK: [[LEX1]] = distinct !DILexicalBlock(scope: [[FUNC]], file: [[FOOCPP]], +// CHECK: [[LEX1]] = distinct !DILexicalBlock(scope: [[FUNC:![0-9]+]], file: [[FOOCPP]], +// CHECK: [[FUNC]] = distinct !DISubprogram(name: "func",{{.*}} isDefinition: true + // CHECK: [[M5]] = !DIImportedEntity(tag: DW_TAG_imported_module, scope: [[FUNC]], entity: [[CTXT]], // CHECK: [[M6]] = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: [[FUNC]], entity: [[FOO:!"_ZTSN1A1B3fooE"]], line: 27) // CHECK: [[M7]] = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: [[FUNC]], entity: [[BAR:!"_ZTSN1A1B3barE"]] -// CHECK: [[M8]] = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: [[FUNC]], entity: [[F1]] +// CHECK: [[M8]] = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: [[FUNC]], entity: [[F1:![0-9]+]] +// CHECK: [[F1:![0-9]+]] = distinct !DISubprogram(name: "f1",{{.*}} line: 4 +// CHECK-SAME: isDefinition: true // CHECK: [[M9]] = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: [[FUNC]], entity: [[I]] // CHECK: [[M10]] = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: [[FUNC]], entity: [[BAZ:![0-9]+]] // CHECK: [[BAZ]] = !DIDerivedType(tag: DW_TAG_typedef, name: "baz", scope: [[NS]], file: [[FOOCPP]], @@ -100,6 +101,7 @@ void B::func_fwd() {} // CHECK-SAME: scope: [[NS]], file: [[FOOCPP]], line: 9 // CHECK: [[M15]] = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: [[FUNC]], entity: [[VAR_FWD:![0-9]+]] // CHECK: [[M16]] = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: [[FUNC]], entity: [[FUNC_FWD:![0-9]+]] +// CHECK: [[FUNC_FWD]] = distinct !DISubprogram(name: "func_fwd",{{.*}} line: 47,{{.*}} isDefinition: true // CHECK: [[M17]] = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: [[CTXT]], entity: [[I]] // CHECK-GMLT: [[CU:![0-9]+]] = distinct !DICompileUnit( diff --git a/test/CodeGenCXX/debug-info-ptr-to-member-function.cpp b/test/CodeGenCXX/debug-info-ptr-to-member-function.cpp index cac16b60089..a7e02e48132 100644 --- a/test/CodeGenCXX/debug-info-ptr-to-member-function.cpp +++ b/test/CodeGenCXX/debug-info-ptr-to-member-function.cpp @@ -7,15 +7,15 @@ struct T { void foo(int (T::*method)()) {} -// A pointer to a member function is a pair of function- and this-pointer. -// CHECK: !DIDerivedType(tag: DW_TAG_ptr_to_member_type, -// DARWIN-X64-SAME: size: 128 -// WIN32-X64-SAME: size: 64 - struct Incomplete; int (Incomplete::**bar)(); +// A pointer to a member function is a pair of function- and this-pointer. // CHECK: !DIDerivedType(tag: DW_TAG_ptr_to_member_type, // DARWIN-X64-SAME: size: 128 // WIN32-X64-NOT: size: // CHECK-SAME: extraData: {{.*}}) + +// CHECK: !DIDerivedType(tag: DW_TAG_ptr_to_member_type, +// DARWIN-X64-SAME: size: 128 +// WIN32-X64-SAME: size: 64 diff --git a/test/CodeGenCXX/debug-info-template-member.cpp b/test/CodeGenCXX/debug-info-template-member.cpp index b94ff05df44..a6ba82aa943 100644 --- a/test/CodeGenCXX/debug-info-template-member.cpp +++ b/test/CodeGenCXX/debug-info-template-member.cpp @@ -50,15 +50,6 @@ inline int add3(int x) { // CHECK: [[ELEM_X]] = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: !"_ZTS4elem" // CHECK-SAME: baseType: !"_ZTS4virtI4elemE" -// Check that the member function template specialization and implicit special -// members (the default ctor) refer to their class by scope, even though they -// didn't appear in the class's member list (C_MEM). This prevents the functions -// from being added to type units, while still appearing in the type -// declaration/reference in the compile unit. -// CHECK: !DISubprogram(name: "MyClass" -// CHECK-SAME: scope: !"_ZTS7MyClass" -// CHECK: !DISubprogram(name: "add<2>" -// CHECK-SAME: scope: !"_ZTS7MyClass" template struct outer { @@ -98,3 +89,13 @@ inline void f1() { void f2() { virt d; // emit 'virt' } + +// Check that the member function template specialization and implicit special +// members (the default ctor) refer to their class by scope, even though they +// didn't appear in the class's member list (C_MEM). This prevents the functions +// from being added to type units, while still appearing in the type +// declaration/reference in the compile unit. +// CHECK: !DISubprogram(name: "MyClass" +// CHECK-SAME: scope: !"_ZTS7MyClass" +// CHECK: !DISubprogram(name: "add<2>" +// CHECK-SAME: scope: !"_ZTS7MyClass" diff --git a/test/CodeGenCXX/debug-info.cpp b/test/CodeGenCXX/debug-info.cpp index 29ed9e93156..394d8792fd8 100644 --- a/test/CodeGenCXX/debug-info.cpp +++ b/test/CodeGenCXX/debug-info.cpp @@ -114,9 +114,6 @@ foo func(foo f) { // CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "b" // CHECK-SAME: DIFlagFwdDecl -// CHECK: [[FUNC:![0-9]+]] = distinct !DISubprogram(name: "func", linkageName: "_ZN7pr147634funcENS_3fooE" -// CHECK-SAME: type: [[FUNC_TYPE:![0-9]*]] -// CHECK-SAME: isDefinition: true } void foo() { @@ -141,6 +138,10 @@ incomplete (*x)[3]; // CHECK-SAME: baseType: !"_ZTSN6pr960810incompleteE" } +// CHECK: [[FUNC:![0-9]+]] = distinct !DISubprogram(name: "func", linkageName: "_ZN7pr147634funcENS_3fooE" +// CHECK-SAME: type: [[FUNC_TYPE:![0-9]*]] +// CHECK-SAME: isDefinition: true + // For some reason function arguments ended up down here // CHECK: ![[F]] = !DILocalVariable(name: "f", arg: 1, scope: [[FUNC]] // CHECK-SAME: type: !"[[FOO]]" diff --git a/test/CodeGenCXX/debug-lambda-expressions.cpp b/test/CodeGenCXX/debug-lambda-expressions.cpp index a022fad1b6b..3efdee54c98 100644 --- a/test/CodeGenCXX/debug-lambda-expressions.cpp +++ b/test/CodeGenCXX/debug-lambda-expressions.cpp @@ -19,18 +19,26 @@ int d(int x) { D y[10]; return [x,y] { return y[x].x; }(); } // CHECK: ![[INT:[0-9]+]] = !DIBasicType(name: "int" -// A: 10 -// CHECK: ![[A_FUNC:.*]] = distinct !DISubprogram(name: "a"{{.*}}, line: [[A_LINE:[0-9]+]]{{.*}}, isDefinition: true - -// B: 14 -// CHECK: ![[B_FUNC:.*]] = distinct !DISubprogram(name: "b"{{.*}}, line: [[B_LINE:[0-9]+]]{{.*}}, isDefinition: true - -// C: 17 -// CHECK: ![[C_FUNC:.*]] = distinct !DISubprogram(name: "c"{{.*}}, line: [[C_LINE:[0-9]+]]{{.*}}, isDefinition: true +// CVAR: +// CHECK: !DIGlobalVariable(name: "cvar" +// CHECK-SAME: line: [[CVAR_LINE:[0-9]+]] +// CHECK-SAME: type: ![[CVAR_T:[0-9]+]] +// CHECK: ![[CVAR_T]] = !DICompositeType(tag: DW_TAG_class_type +// CHECK-SAME: line: [[CVAR_LINE]], +// CHECK-SAME: elements: ![[CVAR_ARGS:[0-9]+]] +// CHECK: ![[CVAR_ARGS]] = !{!{{[0-9]+}}} -// D: 18 -// CHECK: ![[D_FUNC:.*]] = distinct !DISubprogram(name: "d"{{.*}}, line: [[D_LINE:[0-9]+]]{{.*}}, isDefinition: true +// VAR: +// CHECK: !DIGlobalVariable(name: "var" +// CHECK-SAME: line: [[VAR_LINE:[0-9]+]] +// CHECK-SAME: type: ![[VAR_T:[0-9]+]] +// CHECK: ![[VAR_T]] = !DICompositeType(tag: DW_TAG_class_type +// CHECK-SAME: line: [[VAR_LINE]], +// CHECK-SAME: elements: ![[VAR_ARGS:[0-9]+]] +// CHECK: ![[VAR_ARGS]] = !{!{{[0-9]+}}} +// A: 10 +// CHECK: ![[A_FUNC:.*]] = distinct !DISubprogram(name: "a"{{.*}}, line: [[A_LINE:[0-9]+]]{{.*}}, isDefinition: true // Back to A. -- 78 // CHECK: ![[LAM_A:.*]] = !DICompositeType(tag: DW_TAG_class_type{{.*}}, scope: ![[A_FUNC]]{{.*}}, line: [[A_LINE]], @@ -41,6 +49,9 @@ int d(int x) { D y[10]; return [x,y] { return y[x].x; }(); } // CHECK-SAME: line: [[A_LINE]] // CHECK-SAME: DIFlagPublic +// B: 14 +// CHECK: ![[B_FUNC:.*]] = distinct !DISubprogram(name: "b"{{.*}}, line: [[B_LINE:[0-9]+]]{{.*}}, isDefinition: true + // Back to B. -- 67 // CHECK: ![[LAM_B:.*]] = !DICompositeType(tag: DW_TAG_class_type{{.*}}, scope: ![[B_FUNC]]{{.*}}, line: [[B_LINE]], // CHECK-SAME: elements: ![[LAM_B_ARGS:[0-9]+]] @@ -54,6 +65,9 @@ int d(int x) { D y[10]; return [x,y] { return y[x].x; }(); } // CHECK-SAME: line: [[B_LINE]] // CHECK-SAME: DIFlagPublic +// C: 17 +// CHECK: ![[C_FUNC:.*]] = distinct !DISubprogram(name: "c"{{.*}}, line: [[C_LINE:[0-9]+]]{{.*}}, isDefinition: true + // Back to C. -- 55 // CHECK: ![[LAM_C:.*]] = !DICompositeType(tag: DW_TAG_class_type{{.*}}, scope: ![[C_FUNC]]{{.*}}, line: [[C_LINE]], // CHECK-SAME: elements: ![[LAM_C_ARGS:[0-9]+]] @@ -68,6 +82,9 @@ int d(int x) { D y[10]; return [x,y] { return y[x].x; }(); } // CHECK-SAME: line: [[C_LINE]] // CHECK-SAME: DIFlagPublic +// D: 18 +// CHECK: ![[D_FUNC:.*]] = distinct !DISubprogram(name: "d"{{.*}}, line: [[D_LINE:[0-9]+]]{{.*}}, isDefinition: true + // Back to D. -- 24 // CHECK: ![[LAM_D:.*]] = !DICompositeType(tag: DW_TAG_class_type{{.*}}, scope: ![[D_FUNC]]{{.*}}, line: [[D_LINE]], // CHECK-SAME: elements: ![[LAM_D_ARGS:[0-9]+]] @@ -82,21 +99,3 @@ int d(int x) { D y[10]; return [x,y] { return y[x].x; }(); } // CHECK-SAME: scope: ![[LAM_D]] // CHECK-SAME: line: [[D_LINE]] // CHECK-SAME: DIFlagPublic - -// CVAR: -// CHECK: !DIGlobalVariable(name: "cvar" -// CHECK-SAME: line: [[CVAR_LINE:[0-9]+]] -// CHECK-SAME: type: ![[CVAR_T:[0-9]+]] -// CHECK: ![[CVAR_T]] = !DICompositeType(tag: DW_TAG_class_type -// CHECK-SAME: line: [[CVAR_LINE]], -// CHECK-SAME: elements: ![[CVAR_ARGS:[0-9]+]] -// CHECK: ![[CVAR_ARGS]] = !{!{{[0-9]+}}} - -// VAR: -// CHECK: !DIGlobalVariable(name: "var" -// CHECK-SAME: line: [[VAR_LINE:[0-9]+]] -// CHECK-SAME: type: ![[VAR_T:[0-9]+]] -// CHECK: ![[VAR_T]] = !DICompositeType(tag: DW_TAG_class_type -// CHECK-SAME: line: [[VAR_LINE]], -// CHECK-SAME: elements: ![[VAR_ARGS:[0-9]+]] -// CHECK: ![[VAR_ARGS]] = !{!{{[0-9]+}}} diff --git a/test/CodeGenCXX/debug-lambda-this.cpp b/test/CodeGenCXX/debug-lambda-this.cpp index 0c413449a3e..e7ea68c1a35 100644 --- a/test/CodeGenCXX/debug-lambda-this.cpp +++ b/test/CodeGenCXX/debug-lambda-this.cpp @@ -12,10 +12,10 @@ int D::d(int x) { }(); } +// CHECK: ![[POINTER:.*]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !"_ZTS1D", size: 64, align: 64) // CHECK: !DIDerivedType(tag: DW_TAG_member, name: "this", // CHECK-SAME: line: 11 -// CHECK-SAME: baseType: ![[POINTER:[0-9]+]] +// CHECK-SAME: baseType: ![[POINTER]] // CHECK-SAME: size: 64, align: 64 // CHECK-NOT: offset: 0 // CHECK-SAME: ){{$}} -// CHECK: ![[POINTER]] = !DIDerivedType(tag: DW_TAG_pointer_type diff --git a/test/CodeGenCXX/linetable-virtual-variadic.cpp b/test/CodeGenCXX/linetable-virtual-variadic.cpp index 8d1bf47814f..6f966416867 100644 --- a/test/CodeGenCXX/linetable-virtual-variadic.cpp +++ b/test/CodeGenCXX/linetable-virtual-variadic.cpp @@ -15,9 +15,5 @@ void Derived::VariadicFunction(...) { } // CHECK-LABEL: define void @_ZT{{.+}}N7Derived16VariadicFunctionEz( // CHECK: ret void, !dbg ![[LOC:[0-9]+]] // -// CHECK: !llvm.dbg.cu = !{![[CU:[0-9]+]]} -// -// CHECK: ![[CU]] = distinct !DICompileUnit({{.*}} subprograms: ![[SPs:[0-9]+]] -// CHECK: ![[SPs]] = !{![[SP]]} // CHECK: ![[SP]] = distinct !DISubprogram(name: "VariadicFunction" // CHECK: ![[LOC]] = !DILocation({{.*}}scope: ![[SP]]) diff --git a/test/CodeGenObjC/debug-info-block-type.m b/test/CodeGenObjC/debug-info-block-type.m index 1f137ed9dfc..565bc86e8b8 100644 --- a/test/CodeGenObjC/debug-info-block-type.m +++ b/test/CodeGenObjC/debug-info-block-type.m @@ -1,18 +1,17 @@ // RUN: %clang_cc1 -emit-llvm -fblocks -debug-info-kind=limited -triple x86_64-apple-darwin14 -x objective-c < %s -o - | FileCheck %s -#define nil ((void*) 0) -typedef signed char BOOL; -// CHECK: ![[BOOL:[0-9]+]] = !DIDerivedType(tag: DW_TAG_typedef, name: "BOOL" -// CHECK-SAME: line: [[@LINE-2]] -// CHECK: ![[ID:[0-9]+]] = !DIDerivedType(tag: DW_TAG_typedef, name: "id" - -typedef BOOL (^SomeKindOfPredicate)(id obj); // CHECK: !DIDerivedType(tag: DW_TAG_member, name: "__FuncPtr" // CHECK-SAME: baseType: ![[PTR:[0-9]+]] // CHECK: ![[PTR]] = !DIDerivedType(tag: DW_TAG_pointer_type, // CHECK-SAME: baseType: ![[FNTYPE:[0-9]+]] // CHECK: ![[FNTYPE]] = !DISubroutineType(types: ![[ARGS:[0-9]+]]) -// CHECK: ![[ARGS]] = !{![[BOOL]], ![[ID]]} +// CHECK: ![[ARGS]] = !{![[BOOL:.*]], ![[ID:.*]]} +#define nil ((void*) 0) +typedef signed char BOOL; +// CHECK: ![[BOOL]] = !DIDerivedType(tag: DW_TAG_typedef, name: "BOOL" +// CHECK-SAME: line: [[@LINE-2]] +// CHECK: ![[ID]] = !DIDerivedType(tag: DW_TAG_typedef, name: "id" +typedef BOOL (^SomeKindOfPredicate)(id obj); int main() { SomeKindOfPredicate p = ^BOOL(id obj) { return obj != nil; }; diff --git a/test/CodeGenObjC/debug-property-synth.m b/test/CodeGenObjC/debug-property-synth.m index 74ee775f751..45bf77067c8 100644 --- a/test/CodeGenObjC/debug-property-synth.m +++ b/test/CodeGenObjC/debug-property-synth.m @@ -19,8 +19,8 @@ @interface I { // CHECK: load {{.*}}, !dbg ![[DBG2:[0-9]+]] // // CHECK: !DISubprogram(name: "-[I p1]",{{.*}} line: [[@LINE+4]],{{.*}} isLocal: true, isDefinition: true -// CHECK: !DISubprogram(name: "-[I setP1:]",{{.*}} line: [[@LINE+3]],{{.*}} isLocal: true, isDefinition: true -// CHECK: ![[DBG1]] = !DILocation(line: [[@LINE+2]], +// CHECK: ![[DBG1]] = !DILocation(line: [[@LINE+3]], +// CHECK: !DISubprogram(name: "-[I setP1:]",{{.*}} line: [[@LINE+2]],{{.*}} isLocal: true, isDefinition: true // CHECK: ![[DBG2]] = !DILocation(line: [[@LINE+1]], @property int p1; @end diff --git a/test/Modules/ExtDebugInfo.m b/test/Modules/ExtDebugInfo.m index 5c3c7112b8c..d758bfda831 100644 --- a/test/Modules/ExtDebugInfo.m +++ b/test/Modules/ExtDebugInfo.m @@ -29,17 +29,12 @@ int foo(ObjCClass *c) { return [c property]; } -// CHECK-NOT: !DICompositeType(tag: DW_TAG_structure_type, -// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "ObjCClass", -// CHECK-SAME: scope: ![[MOD:[0-9]+]], -// CHECK-SAME: flags: DIFlagFwdDecl) -// CHECK-NOT: !DICompositeType(tag: DW_TAG_structure_type, -// CHECK: ![[MOD]] = !DIModule(scope: null, name: "DebugObjC - // CHECK: !DIGlobalVariable(name: "GlobalUnion", // CHECK-SAME: type: ![[GLOBAL_UNION:[0-9]+]] +// CHECK: ![[MOD:.*]] = !DIModule(scope: null, name: "DebugObjC // CHECK: ![[GLOBAL_UNION]] = !DICompositeType(tag: DW_TAG_union_type, // CHECK-SAME: elements: !{{[0-9]+}}) + // CHECK: !DIGlobalVariable(name: "GlobalStruct", // CHECK-SAME: type: ![[GLOBAL_STRUCT:[0-9]+]] // CHECK: ![[GLOBAL_STRUCT]] = !DICompositeType(tag: DW_TAG_structure_type, @@ -49,15 +44,21 @@ int foo(ObjCClass *c) { // CHECK-SAME: baseType: ![[TD_UNION:.*]]) // CHECK: ![[TD_UNION]] = !DICompositeType(tag: DW_TAG_union_type, // CHECK-SAME: flags: DIFlagFwdDecl) + // CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "TypedefEnum", // CHECK-SAME: baseType: ![[TD_ENUM:.*]]) // CHECK: ![[TD_ENUM]] = !DICompositeType(tag: DW_TAG_enumeration_type, // CHECK-SAME: flags: DIFlagFwdDecl) + // CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "TypedefStruct", // CHECK-SAME: baseType: ![[TD_STRUCT:.*]]) // CHECK: ![[TD_STRUCT]] = !DICompositeType(tag: DW_TAG_structure_type, // CHECK-SAME: flags: DIFlagFwdDecl) +// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "ObjCClass", +// CHECK-SAME: scope: ![[MOD]], +// CHECK-SAME: flags: DIFlagFwdDecl) + // CHECK-NOT: !DICompositeType(tag: DW_TAG_structure_type, // CHECK: !DICompositeType(tag: DW_TAG_enumeration_type, // CHECK-SAME: scope: ![[MOD]], diff --git a/test/Modules/ModuleDebugInfo.cpp b/test/Modules/ModuleDebugInfo.cpp index 993e72a7706..2f8b7e6a710 100644 --- a/test/Modules/ModuleDebugInfo.cpp +++ b/test/Modules/ModuleDebugInfo.cpp @@ -55,6 +55,9 @@ // CHECK-SAME: name: "Template >" // CHECK-SAME: identifier: "_ZTSN8DebugCXX8TemplateIfNS_6traitsIfEEEE") +// CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "B", +// no mangled name here yet. + // CHECK: !DICompositeType(tag: DW_TAG_class_type, name: "FwdVirtual" // CHECK-SAME: elements: // CHECK-SAME: identifier: "_ZTS10FwdVirtual") @@ -78,9 +81,6 @@ // CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "FloatInstatiation" // no mangled name here yet. -// CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "B", -// no mangled name here yet. - // CHECK: !DICompositeType(tag: DW_TAG_union_type, // CHECK-SAME-NOT: name: diff --git a/test/Modules/ModuleDebugInfo.m b/test/Modules/ModuleDebugInfo.m index 6bca67656ae..873f197522a 100644 --- a/test/Modules/ModuleDebugInfo.m +++ b/test/Modules/ModuleDebugInfo.m @@ -34,11 +34,16 @@ // CHECK-SAME-NOT: name: // CHECK-SAME: elements: -// CHECK: !DICompositeType(tag: DW_TAG_structure_type, -// CHECK-SAME: name: "FwdDecl", +// CHECK: !DISubprogram(name: "+[ObjCClass classMethod]", +// CHECK-SAME: scope: ![[MODULE]], + +// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "ObjCClass", // CHECK-SAME: scope: ![[MODULE]], -// CHECK: !DICompositeType(tag: DW_TAG_structure_type, -// CHECK-SAME: name: "ObjCClass", + +// The forward declaration should not be in the module scope. +// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "OpaqueData", file + +// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "FwdDecl", // CHECK-SAME: scope: ![[MODULE]], // CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "FwdDeclared" @@ -66,24 +71,16 @@ // CHECK: !DICompositeType(tag: DW_TAG_structure_type, // CHECK-SAME-NOT: name: -// CHECK: !DISubprogram(name: "+[ObjCClass classMethod]", -// CHECK-SAME: scope: ![[MODULE]], - -// The forward declaration should not be in the module scope. -// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "OpaqueData", file - // CHECK-NEG-NOT: !DICompositeType(tag: DW_TAG_structure_type, name: "PureForwardDecl" // The output order is sublty different for module vs. pch, // so these are checked separately: // -// CHECK2: !DICompositeType(tag: DW_TAG_structure_type, -// CHECK2-SAME: name: "FwdDecl", -// CHECK2: !DICompositeType(tag: DW_TAG_structure_type, -// CHECK2-SAME: name: "ObjCClass", -// CHECK2: !DIObjCProperty(name: "property", -// CHECK2: !DIDerivedType(tag: DW_TAG_member, name: "ivar" -// CHECK2: !DIDerivedType(tag: DW_TAG_typedef, name: "InnerEnum" // CHECK2: !DISubprogram(name: "+[ObjCClass classMethod]" // CHECK2: !DISubprogram(name: "-[ObjCClass instanceMethodWithInt:]" +// CHECK2: !DICompositeType(tag: DW_TAG_structure_type, name: "ObjCClass", +// CHECK2: !DIObjCProperty(name: "property", +// CHECK2: !DIDerivedType(tag: DW_TAG_member, name: "ivar" // CHECK2: !DISubprogram(name: "-[Category(Category) categoryMethod]" +// CHECK2: !DICompositeType(tag: DW_TAG_structure_type, name: "FwdDecl", +// CHECK2: !DIDerivedType(tag: DW_TAG_typedef, name: "InnerEnum" From 97b6334a62800587c8252216d517b52d1a1d08b1 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Fri, 15 Apr 2016 16:21:23 +0000 Subject: [PATCH 499/742] Fix testcase for MSVC targets where the output ordering is different. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@266449 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 2785c796d94f1b17157cf4fa9f6f7eda10efc9f6) --- test/CodeGenCXX/debug-info-limited.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/CodeGenCXX/debug-info-limited.cpp b/test/CodeGenCXX/debug-info-limited.cpp index b209e3a850d..4467d20f3de 100644 --- a/test/CodeGenCXX/debug-info-limited.cpp +++ b/test/CodeGenCXX/debug-info-limited.cpp @@ -1,4 +1,5 @@ // RUN: %clang -flimit-debug-info -emit-llvm -g -S %s -o - | FileCheck %s +// RUN: %clang -flimit-debug-info -emit-llvm -g -S %s -o - | FileCheck --check-prefix=CHECK-C %s // CHECK: !DICompositeType(tag: DW_TAG_class_type, name: "A" // CHECK-NOT: DIFlagFwdDecl @@ -27,8 +28,8 @@ int baz(B *b) { } -// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "C" -// CHECK-SAME: flags: DIFlagFwdDecl +// CHECK-C: !DICompositeType(tag: DW_TAG_structure_type, name: "C" +// CHECK-C-SAME: flags: DIFlagFwdDecl struct C { }; From 1b3d4d44c542cc0076a7c543b8236068c52c2e07 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Fri, 8 Apr 2016 22:43:06 +0000 Subject: [PATCH 500/742] Use NoDebug compile units to mark debug metadata used only for sample-based profiling and optimization remarks and indicate that no debug info shall be emitted for these compile units. http://reviews.llvm.org/D18808 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265862 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit cc8e531a17f815659b752594aebab58e08173411) --- lib/CodeGen/CGDebugInfo.cpp | 21 ++++++++++++++----- test/Frontend/optimization-remark.c | 5 +++-- .../profile-sample-use-loc-tracking.c | 5 +++-- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index f475d127b6c..ee3f2641dd2 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -396,16 +396,27 @@ void CGDebugInfo::CreateCompileUnit() { if (LO.ObjC1) RuntimeVers = LO.ObjCRuntime.isNonFragile() ? 2 : 1; + llvm::DICompileUnit::DebugEmissionKind EmissionKind; + switch (DebugKind) { + case codegenoptions::NoDebugInfo: + case codegenoptions::LocTrackingOnly: + EmissionKind = llvm::DICompileUnit::NoDebug; + break; + case codegenoptions::DebugLineTablesOnly: + EmissionKind = llvm::DICompileUnit::LineTablesOnly; + break; + case codegenoptions::LimitedDebugInfo: + case codegenoptions::FullDebugInfo: + EmissionKind = llvm::DICompileUnit::FullDebug; + break; + } + // Create new compile unit. // FIXME - Eliminate TheCU. TheCU = DBuilder.createCompileUnit( LangTag, remapDIPath(MainFileName), remapDIPath(getCurrentDirname()), Producer, LO.Optimize, CGM.getCodeGenOpts().DwarfDebugFlags, RuntimeVers, - CGM.getCodeGenOpts().SplitDwarfFile, - DebugKind <= codegenoptions::DebugLineTablesOnly - ? llvm::DICompileUnit::LineTablesOnly - : llvm::DICompileUnit::FullDebug, - 0 /* DWOid */, DebugKind != codegenoptions::LocTrackingOnly); + CGM.getCodeGenOpts().SplitDwarfFile, EmissionKind, 0 /* DWOid */); } llvm::DIType *CGDebugInfo::CreateType(const BuiltinType *BT) { diff --git a/test/Frontend/optimization-remark.c b/test/Frontend/optimization-remark.c index e5bd75cbdb3..72cad8ff608 100644 --- a/test/Frontend/optimization-remark.c +++ b/test/Frontend/optimization-remark.c @@ -27,9 +27,10 @@ // CHECK: , !dbg ! // CHECK-NOT: DW_TAG_base_type -// But llvm.dbg.cu should be missing (to prevent writing debug info to +// The CU should be marked NoDebug (to prevent writing debug info to // the final output). -// CHECK-NOT: !llvm.dbg.cu = !{ +// CHECK: !llvm.dbg.cu = !{![[CU:.*]]} +// CHECK: ![[CU]] = distinct !DICompileUnit({{.*}}emissionKind: NoDebug int foo(int x, int y) __attribute__((always_inline)); int foo(int x, int y) { return x + y; } diff --git a/test/Frontend/profile-sample-use-loc-tracking.c b/test/Frontend/profile-sample-use-loc-tracking.c index 31faad68d39..6d722c29852 100644 --- a/test/Frontend/profile-sample-use-loc-tracking.c +++ b/test/Frontend/profile-sample-use-loc-tracking.c @@ -10,9 +10,10 @@ // CHECK: , !dbg ! // CHECK-NOT: DW_TAG_base_type -// But llvm.dbg.cu should be missing (to prevent writing debug info to +// The CU should be marked NoDebug (to prevent writing debug info to // the final output). -// CHECK-NOT: !llvm.dbg.cu = !{ +// CHECK: !llvm.dbg.cu = !{![[CU:.*]]} +// CHECK: ![[CU]] = distinct !DICompileUnit({{.*}}emissionKind: NoDebug int bar(int j) { return (j + j - 2) * (j - 2) * j; From 367ca220b37d64e24b9b040461b3354af46b13d9 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka Date: Mon, 18 Apr 2016 18:19:45 +0000 Subject: [PATCH 501/742] [Parser][ObjC] Make sure c++11 in-class initialization is done when the constructor's definition is in an implementation block. Without this commit, ptr doesn't get initialized to null in the following code: struct S { S(); void *ptr = nullptr; }; @implementation I S::S() {} @end rdar://problem/25693624 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@266645 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 3ae2ef0f0bbfcb5829ac2ac4f63ac555d94e3f78) --- lib/Parse/ParseObjc.cpp | 2 ++ test/Parser/objc-default-ctor-init.mm | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 test/Parser/objc-default-ctor-init.mm diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index e47e361c32b..a731187185a 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/lib/Parse/ParseObjc.cpp @@ -3650,6 +3650,8 @@ void Parser::ParseLexedObjCMethodDefs(LexedMethod &LM, bool parseMethod) { else { if (Tok.is(tok::colon)) ParseConstructorInitializer(MCDecl); + else + Actions.ActOnDefaultCtorInitializers(MCDecl); ParseFunctionStatementBody(MCDecl, BodyScope); } diff --git a/test/Parser/objc-default-ctor-init.mm b/test/Parser/objc-default-ctor-init.mm new file mode 100644 index 00000000000..fda8befa377 --- /dev/null +++ b/test/Parser/objc-default-ctor-init.mm @@ -0,0 +1,17 @@ +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.10 -std=c++11 -ast-dump %s | FileCheck %s +// CHECK: CXXCtorInitializer Field {{.*}} 'ptr' 'void *' + +@interface NSObject +@end + +@interface I : NSObject +@end + +struct S { + S(); + void *ptr = nullptr; +}; + +@implementation I +S::S() {} +@end From 4524579cbc1b0e2f38e6ceab8ae78b9f50716785 Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Mon, 18 Apr 2016 18:40:51 +0000 Subject: [PATCH 502/742] Block: Fix a crash when we have type attributes or qualifiers with omitted return type. Emit a warning instead of crashing in IR generation. rdar://22762981 Differential Revision: http://reviews.llvm.org/D18567 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@266648 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 8 ++++ lib/Sema/SemaType.cpp | 45 +++++++++++++++++++++- test/SemaObjC/block-omitted-return-type.m | 44 +++++++++++++++++++++ 3 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 test/SemaObjC/block-omitted-return-type.m diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index d24ef1a9add..86443871c3d 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -8298,4 +8298,12 @@ def warn_objc_redundant_qualified_class_type : Warning< "parameterized class %0 already conforms to the protocols listed; did you " "forget a '*'?">, InGroup; +def warn_block_literal_attributes_on_omitted_return_type : Warning< + "attribute %0 ignored, because it cannot be applied to omitted return type">, + InGroup; + +def warn_block_literal_qualifiers_on_omitted_return_type : Warning< + "'%0' qualifier on omitted return type %1 has no effect">, + InGroup; + } // end of sema component. diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 829506259c9..2872b44c27b 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -734,6 +734,7 @@ static void diagnoseAndRemoveTypeQualifiers(Sema &S, const DeclSpec &DS, // it; they probably didn't mean to specify a redundant qualifier. typedef std::pair QualLoc; for (QualLoc Qual : {QualLoc(DeclSpec::TQ_const, DS.getConstSpecLoc()), + QualLoc(DeclSpec::TQ_restrict, DS.getRestrictSpecLoc()), QualLoc(DeclSpec::TQ_volatile, DS.getVolatileSpecLoc()), QualLoc(DeclSpec::TQ_atomic, DS.getAtomicSpecLoc())}) { if (!(RemoveTQs & Qual.first)) @@ -750,6 +751,47 @@ static void diagnoseAndRemoveTypeQualifiers(Sema &S, const DeclSpec &DS, } } +/// Return true if this is omitted block return type. Also check type +/// attributes and type qualifiers when returning true. +static bool checkOmittedBlockReturnType(Sema &S, Declarator &declarator, + QualType Result) { + if (!isOmittedBlockReturnType(declarator)) + return false; + + // Warn if we see type attributes for omitted return type on a block literal. + AttributeList *&attrs = + declarator.getMutableDeclSpec().getAttributes().getListRef(); + AttributeList *prev = nullptr; + for (AttributeList *cur = attrs; cur; cur = cur->getNext()) { + AttributeList &attr = *cur; + // Skip attributes that were marked to be invalid or non-type + // attributes. + if (attr.isInvalid() || !attr.isTypeAttr()) { + prev = cur; + continue; + } + S.Diag(attr.getLoc(), + diag::warn_block_literal_attributes_on_omitted_return_type) + << attr.getName(); + // Remove cur from the list. + if (prev) { + prev->setNext(cur->getNext()); + prev = cur; + } else { + attrs = cur->getNext(); + } + } + + // Warn if we see type qualifiers for omitted return type on a block literal. + const DeclSpec &DS = declarator.getDeclSpec(); + unsigned TypeQuals = DS.getTypeQualifiers(); + diagnoseAndRemoveTypeQualifiers(S, DS, TypeQuals, Result, (unsigned)-1, + diag::warn_block_literal_qualifiers_on_omitted_return_type); + declarator.getMutableDeclSpec().ClearTypeQualifiers(); + + return true; +} + /// Apply Objective-C type arguments to the given type. static QualType applyObjCTypeArgs(Sema &S, SourceLocation loc, QualType type, ArrayRef typeArgs, @@ -1251,7 +1293,8 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { Result = Context.getAutoDeductType(); break; } else if (declarator.getContext() == Declarator::LambdaExprContext || - isOmittedBlockReturnType(declarator)) { + checkOmittedBlockReturnType(S, declarator, + Context.DependentTy)) { Result = Context.DependentTy; break; } diff --git a/test/SemaObjC/block-omitted-return-type.m b/test/SemaObjC/block-omitted-return-type.m new file mode 100644 index 00000000000..20e32e01865 --- /dev/null +++ b/test/SemaObjC/block-omitted-return-type.m @@ -0,0 +1,44 @@ +// RUN: %clang_cc1 %s -fblocks -verify -fsyntax-only + +@interface NSObject +@end + +@interface Test : NSObject +- (void)test; +@end + +@implementation Test +- (void)test +{ + void (^simpleBlock)() = ^ _Nonnull { //expected-warning {{attribute '_Nonnull' ignored, because it cannot be applied to omitted return type}} + return; + }; + void (^simpleBlock2)() = ^ _Nonnull void { //expected-error {{nullability specifier '_Nonnull' cannot be applied to non-pointer type 'void'}} + return; + }; + void (^simpleBlock3)() = ^ _Nonnull (void) { //expected-warning {{attribute '_Nonnull' ignored, because it cannot be applied to omitted return type}} + return; + }; + + void (^simpleBlock4)() = ^ const { //expected-warning {{'const' qualifier on omitted return type '' has no effect}} + return; + }; + void (^simpleBlock5)() = ^ const void { //expected-error {{incompatible block pointer types initializing 'void (^)()' with an expression of type 'const void (^)(void)'}} + return; + }; + void (^simpleBlock6)() = ^ const (void) { //expected-warning {{'const' qualifier on omitted return type '' has no effect}} + return; + }; + void (^simpleBlock7)() = ^ _Nonnull __attribute__((align_value(128))) _Nullable const (void) { // expected-warning {{attribute '_Nullable' ignored, because it cannot be applied to omitted return type}} \ + // expected-warning {{attribute '_Nonnull' ignored, because it cannot be applied to omitted return type}} \ + // expected-warning {{'const' qualifier on omitted return type '' has no effect}} \ + // expected-warning {{'align_value' attribute only applies to variables and typedefs}} + return; + }; + void (^simpleBlock9)() = ^ __attribute__ ((align_value(128))) _Nonnull const (void) { // expected-warning {{attribute '_Nonnull' ignored, because it cannot be applied to omitted return type}} \ + // expected-warning {{'const' qualifier on omitted return type '' has no effect}} \ + // expected-warning {{'align_value' attribute only applies to variables and typedefs}} + return; + }; +} +@end From 2e0d76bc1137f3d4143a8b295fcac36d30e69f3a Mon Sep 17 00:00:00 2001 From: Bob Wilson Date: Wed, 24 Feb 2016 18:38:35 +0000 Subject: [PATCH 503/742] Fix typo in test/CodeGen/object-size.c CHECK line. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@261762 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 1d71b291bc87fc8137bd8c976bba2753e2de45ee) --- test/CodeGen/object-size.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/CodeGen/object-size.c b/test/CodeGen/object-size.c index 610e54150d8..6aee57375a4 100644 --- a/test/CodeGen/object-size.c +++ b/test/CodeGen/object-size.c @@ -505,7 +505,7 @@ void test31() { // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false) gi = __builtin_object_size(ds1[9].snd, 1); - // CHECH: store i32 2 + // CHECK: store i32 2 gi = __builtin_object_size(&ss[9].snd[0], 1); // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false) From a815f941e7f57cf74ecdbdfce255fa152adb3a61 Mon Sep 17 00:00:00 2001 From: "Duncan P. N. Exon Smith" Date: Fri, 25 Mar 2016 00:38:14 +0000 Subject: [PATCH 504/742] CGLoopInfo: Use the MD_loop metadata kind from r264371, NFC Besides a small compile-time speedup, there should be no real functionality change here. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@264372 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 2250bd96d2b6fd52da1f14e6d94c5ecd1eaedf04) --- lib/CodeGen/CGLoopInfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CodeGen/CGLoopInfo.cpp b/lib/CodeGen/CGLoopInfo.cpp index 0afe7dbb9f1..25f1e2e186f 100644 --- a/lib/CodeGen/CGLoopInfo.cpp +++ b/lib/CodeGen/CGLoopInfo.cpp @@ -237,7 +237,7 @@ void LoopInfoStack::InsertHelper(Instruction *I) const { if (TerminatorInst *TI = dyn_cast(I)) { for (unsigned i = 0, ie = TI->getNumSuccessors(); i < ie; ++i) if (TI->getSuccessor(i) == L.getHeader()) { - TI->setMetadata("llvm.loop", L.getLoopID()); + TI->setMetadata(llvm::LLVMContext::MD_loop, L.getLoopID()); break; } return; From 080bcfefed040f247cbb87020ca1385282f91731 Mon Sep 17 00:00:00 2001 From: "Duncan P. N. Exon Smith" Date: Sun, 17 Apr 2016 07:45:08 +0000 Subject: [PATCH 505/742] DebugInfo: Make DICompositeTypes distinct most of the time Since elements of most kinds of DICompositeType have back references, most are involved in uniquing cycles. Except via the ODR 'identifier:' field, which doesn't care about the storage type (see r266549), they have no hope of being uniqued. Distinct nodes are far more efficient, so use them for most kinds of DICompositeType definitions (i.e., when DIType::isForwardDecl is false). The exceptions: - DW_TAG_array_type, since their elements never have back-references and they never have ODR 'identifier:' fields; - DW_TAG_enumeration_type when there is no ODR 'identifier:' field, since their elements usually don't have back-references. This breaks the last major uniquing cycle I'm aware of in the debug info graph. The impact won't be enormous for C++ because references to ODR-uniqued nodes still use string-based DITypeRefs; but this should prevent a regression in C++ when we drop the string-based references. This wouldn't have been reasonable until r266549, when composite types stopped relying on being uniqued by structural equivalence to prevent blow-ups at LTO time. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@266556 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 70fae264e38b9d898d3726f366e4e2da020edaa8) Conflicts: test/Modules/ModuleDebugInfo.m --- lib/CodeGen/CGDebugInfo.cpp | 24 +++++++++++++++++++ .../CodeGenCXX/debug-info-anon-union-vars.cpp | 2 +- test/CodeGenCXX/debug-info-artificial-arg.cpp | 2 +- test/CodeGenCXX/debug-info-class.cpp | 2 +- test/CodeGenCXX/debug-info-template-limit.cpp | 2 +- .../CodeGenCXX/debug-info-template-member.cpp | 6 ++--- test/CodeGenCXX/debug-info-template-quals.cpp | 2 +- test/CodeGenCXX/debug-info-template.cpp | 4 ++-- test/CodeGenCXX/debug-lambda-expressions.cpp | 12 +++++----- test/Modules/ExtDebugInfo.cpp | 4 ++-- test/Modules/ExtDebugInfo.m | 4 ++-- test/Modules/ModuleDebugInfo.m | 4 ++-- 12 files changed, 46 insertions(+), 22 deletions(-) diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index ee3f2641dd2..23735294ad4 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -2397,6 +2397,30 @@ llvm::DICompositeType *CGDebugInfo::CreateLimitedType(const RecordType *Ty) { getTagForRecord(RD), RDName, RDContext, DefUnit, Line, 0, Size, Align, 0, FullName); + // Elements of composite types usually have back to the type, creating + // uniquing cycles. Distinct nodes are more efficient. + switch (RealDecl->getTag()) { + default: + llvm_unreachable("invalid composite type tag"); + + case llvm::dwarf::DW_TAG_array_type: + case llvm::dwarf::DW_TAG_enumeration_type: + // Array elements and most enumeration elements don't have back references, + // so they don't tend to be involved in uniquing cycles and there is some + // chance of merging them when linking together two modules. Only make + // them distinct if they are ODR-uniqued. + if (FullName.empty()) + break; + + case llvm::dwarf::DW_TAG_structure_type: + case llvm::dwarf::DW_TAG_union_type: + case llvm::dwarf::DW_TAG_class_type: + // Immediatley resolve to a distinct node. + RealDecl = + llvm::MDNode::replaceWithDistinct(llvm::TempDICompositeType(RealDecl)); + break; + } + RegionMap[Ty->getDecl()].reset(RealDecl); TypeCache[QualType(Ty, 0).getAsOpaquePtr()].reset(RealDecl); diff --git a/test/CodeGenCXX/debug-info-anon-union-vars.cpp b/test/CodeGenCXX/debug-info-anon-union-vars.cpp index 80742250d84..b844d429447 100644 --- a/test/CodeGenCXX/debug-info-anon-union-vars.cpp +++ b/test/CodeGenCXX/debug-info-anon-union-vars.cpp @@ -56,7 +56,7 @@ void instantiate(int x) { // CHECK: !DILocalVariable( // CHECK-NOT: name: // CHECK: type: ![[UNION:[0-9]+]] -// CHECK: ![[UNION]] = !DICompositeType(tag: DW_TAG_union_type, +// CHECK: ![[UNION]] = distinct !DICompositeType(tag: DW_TAG_union_type, // CHECK-NOT: name: // CHECK: elements // CHECK: !DIDerivedType(tag: DW_TAG_member, name: "i", scope: ![[UNION]], diff --git a/test/CodeGenCXX/debug-info-artificial-arg.cpp b/test/CodeGenCXX/debug-info-artificial-arg.cpp index c840df672aa..e927dc5d734 100644 --- a/test/CodeGenCXX/debug-info-artificial-arg.cpp +++ b/test/CodeGenCXX/debug-info-artificial-arg.cpp @@ -22,7 +22,7 @@ int main(int argc, char **argv) { A reallyA (500); } -// CHECK: ![[CLASSTYPE:.*]] = !DICompositeType(tag: DW_TAG_class_type, name: "A", +// CHECK: ![[CLASSTYPE:.*]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "A", // CHECK-SAME: identifier: "_ZTS1A" // CHECK: ![[ARTARG:.*]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !"_ZTS1A", // CHECK-SAME: DIFlagArtificial diff --git a/test/CodeGenCXX/debug-info-class.cpp b/test/CodeGenCXX/debug-info-class.cpp index ed52fd05238..ece5c945449 100644 --- a/test/CodeGenCXX/debug-info-class.cpp +++ b/test/CodeGenCXX/debug-info-class.cpp @@ -101,7 +101,7 @@ int main(int argc, char **argv) { // CHECK: ![[INT:[0-9]+]] = !DIBasicType(name: "int" -// CHECK: [[C:![0-9]*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "C", +// CHECK: [[C:![0-9]*]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "C", // CHECK-NOT: DIFlagFwdDecl // CHECK-SAME: elements: [[C_MEM:![0-9]*]] // CHECK-SAME: vtableHolder: !"_ZTS1C" diff --git a/test/CodeGenCXX/debug-info-template-limit.cpp b/test/CodeGenCXX/debug-info-template-limit.cpp index 5c4ac0cc3e1..7e3a4a6959c 100644 --- a/test/CodeGenCXX/debug-info-template-limit.cpp +++ b/test/CodeGenCXX/debug-info-template-limit.cpp @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -emit-llvm -debug-info-kind=limited -triple %itanium_abi_triple %s -o - | FileCheck %s // Check that this pointer type is TC -// CHECK: ![[LINE:[0-9]+]] = !DICompositeType(tag: DW_TAG_class_type, name: "TC"{{.*}}, identifier: "_ZTS2TCIiE") +// CHECK: ![[LINE:[0-9]+]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "TC"{{.*}}, identifier: "_ZTS2TCIiE") // CHECK: !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !"_ZTS2TCIiE" template diff --git a/test/CodeGenCXX/debug-info-template-member.cpp b/test/CodeGenCXX/debug-info-template-member.cpp index a6ba82aa943..dcc464629fd 100644 --- a/test/CodeGenCXX/debug-info-template-member.cpp +++ b/test/CodeGenCXX/debug-info-template-member.cpp @@ -24,7 +24,7 @@ inline int add3(int x) { // CHECK-SAME: type: [[FOO_FUNC_TYPE:![0-9]*]] // CHECK: [[FOO_FUNC_TYPE]] = !DISubroutineType(types: [[FOO_FUNC_PARAMS:![0-9]*]]) // CHECK: [[FOO_FUNC_PARAMS]] = !{null, !{{[0-9]*}}, !"[[OUTER_FOO_INNER_ID:.*]]"} -// CHECK: !{{[0-9]*}} = !DICompositeType(tag: DW_TAG_structure_type, name: "inner"{{.*}}, identifier: "[[OUTER_FOO_INNER_ID]]") +// CHECK: !{{[0-9]*}} = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "inner"{{.*}}, identifier: "[[OUTER_FOO_INNER_ID]]") // CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "virt" // CHECK-SAME: elements: [[VIRT_MEM:![0-9]*]] @@ -34,7 +34,7 @@ inline int add3(int x) { // CHECK: [[VIRT_TEMP_PARAM]] = !{[[VIRT_T:![0-9]*]]} // CHECK: [[VIRT_T]] = !DITemplateTypeParameter(name: "T", type: !"_ZTS4elem") -// CHECK: [[C:![0-9]*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "MyClass" +// CHECK: [[C:![0-9]*]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "MyClass" // CHECK-SAME: elements: [[C_MEM:![0-9]*]] // CHECK-SAME: vtableHolder: !"_ZTS7MyClass" // CHECK-SAME: identifier: "_ZTS7MyClass") @@ -43,7 +43,7 @@ inline int add3(int x) { // CHECK: [[C_FUNC]] = !DISubprogram(name: "func",{{.*}} line: 7, -// CHECK: [[ELEM:![0-9]*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "elem" +// CHECK: [[ELEM:![0-9]*]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "elem" // CHECK-SAME: elements: [[ELEM_MEM:![0-9]*]] // CHECK-SAME: identifier: "_ZTS4elem" // CHECK: [[ELEM_MEM]] = !{[[ELEM_X:![0-9]*]]} diff --git a/test/CodeGenCXX/debug-info-template-quals.cpp b/test/CodeGenCXX/debug-info-template-quals.cpp index 1e8bdb1ad71..881bb0e2005 100644 --- a/test/CodeGenCXX/debug-info-template-quals.cpp +++ b/test/CodeGenCXX/debug-info-template-quals.cpp @@ -15,7 +15,7 @@ void foo (const char *c) { str.assign(c, str); } -// CHECK: [[BS:.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "basic_string" +// CHECK: [[BS:.*]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "basic_string" // CHECK-SAME: line: 4 // CHECK-SAME: size: 8, align: 8 // CHECK: [[TYPE:![0-9]*]] = !DISubroutineType(types: [[ARGS:.*]]) diff --git a/test/CodeGenCXX/debug-info-template.cpp b/test/CodeGenCXX/debug-info-template.cpp index 74adef9a5f7..569357c0619 100644 --- a/test/CodeGenCXX/debug-info-template.cpp +++ b/test/CodeGenCXX/debug-info-template.cpp @@ -6,7 +6,7 @@ // CHECK: [[RETAIN]] = !{!{{[0-9]]*}}, [[FOO:![0-9]*]], -// CHECK: [[TC:![0-9]*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "TC" +// CHECK: [[TC:![0-9]*]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "TC" // CHECK-SAME: templateParams: [[TCARGS:![0-9]*]] // CHECK: [[TCARGS]] = !{[[TCARG1:![0-9]*]], [[TCARG2:![0-9]*]], [[TCARG3:![0-9]*]], [[TCARG4:![0-9]*]], [[TCARG5:![0-9]*]], [[TCARG6:![0-9]*]], [[TCARG7:![0-9]*]]} // @@ -48,7 +48,7 @@ // We could just emit a declaration of 'foo' here, rather than the entire // definition (same goes for any time we emit a member (function or data) // pointer type) -// CHECK: [[FOO]] = !DICompositeType(tag: DW_TAG_structure_type, name: "foo", {{.*}}identifier: "_ZTS3foo") +// CHECK: [[FOO]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "foo", {{.*}}identifier: "_ZTS3foo") // CHECK: !DISubprogram(name: "f", linkageName: "_ZN3foo1fEv", {{.*}}type: [[FTYPE:![0-9]*]] // diff --git a/test/CodeGenCXX/debug-lambda-expressions.cpp b/test/CodeGenCXX/debug-lambda-expressions.cpp index 3efdee54c98..985234f481f 100644 --- a/test/CodeGenCXX/debug-lambda-expressions.cpp +++ b/test/CodeGenCXX/debug-lambda-expressions.cpp @@ -23,7 +23,7 @@ int d(int x) { D y[10]; return [x,y] { return y[x].x; }(); } // CHECK: !DIGlobalVariable(name: "cvar" // CHECK-SAME: line: [[CVAR_LINE:[0-9]+]] // CHECK-SAME: type: ![[CVAR_T:[0-9]+]] -// CHECK: ![[CVAR_T]] = !DICompositeType(tag: DW_TAG_class_type +// CHECK: ![[CVAR_T]] = distinct !DICompositeType(tag: DW_TAG_class_type // CHECK-SAME: line: [[CVAR_LINE]], // CHECK-SAME: elements: ![[CVAR_ARGS:[0-9]+]] // CHECK: ![[CVAR_ARGS]] = !{!{{[0-9]+}}} @@ -32,7 +32,7 @@ int d(int x) { D y[10]; return [x,y] { return y[x].x; }(); } // CHECK: !DIGlobalVariable(name: "var" // CHECK-SAME: line: [[VAR_LINE:[0-9]+]] // CHECK-SAME: type: ![[VAR_T:[0-9]+]] -// CHECK: ![[VAR_T]] = !DICompositeType(tag: DW_TAG_class_type +// CHECK: ![[VAR_T]] = distinct !DICompositeType(tag: DW_TAG_class_type // CHECK-SAME: line: [[VAR_LINE]], // CHECK-SAME: elements: ![[VAR_ARGS:[0-9]+]] // CHECK: ![[VAR_ARGS]] = !{!{{[0-9]+}}} @@ -41,7 +41,7 @@ int d(int x) { D y[10]; return [x,y] { return y[x].x; }(); } // CHECK: ![[A_FUNC:.*]] = distinct !DISubprogram(name: "a"{{.*}}, line: [[A_LINE:[0-9]+]]{{.*}}, isDefinition: true // Back to A. -- 78 -// CHECK: ![[LAM_A:.*]] = !DICompositeType(tag: DW_TAG_class_type{{.*}}, scope: ![[A_FUNC]]{{.*}}, line: [[A_LINE]], +// CHECK: ![[LAM_A:.*]] = distinct !DICompositeType(tag: DW_TAG_class_type{{.*}}, scope: ![[A_FUNC]]{{.*}}, line: [[A_LINE]], // CHECK-SAME: elements: ![[LAM_A_ARGS:[0-9]+]] // CHECK: ![[LAM_A_ARGS]] = !{![[CON_LAM_A:[0-9]+]]} // CHECK: ![[CON_LAM_A]] = !DISubprogram(name: "operator()" @@ -53,7 +53,7 @@ int d(int x) { D y[10]; return [x,y] { return y[x].x; }(); } // CHECK: ![[B_FUNC:.*]] = distinct !DISubprogram(name: "b"{{.*}}, line: [[B_LINE:[0-9]+]]{{.*}}, isDefinition: true // Back to B. -- 67 -// CHECK: ![[LAM_B:.*]] = !DICompositeType(tag: DW_TAG_class_type{{.*}}, scope: ![[B_FUNC]]{{.*}}, line: [[B_LINE]], +// CHECK: ![[LAM_B:.*]] = distinct !DICompositeType(tag: DW_TAG_class_type{{.*}}, scope: ![[B_FUNC]]{{.*}}, line: [[B_LINE]], // CHECK-SAME: elements: ![[LAM_B_ARGS:[0-9]+]] // CHECK: ![[LAM_B_ARGS]] = !{![[CAP_B:[0-9]+]], ![[CON_LAM_B:[0-9]+]]} // CHECK: ![[CAP_B]] = !DIDerivedType(tag: DW_TAG_member, name: "x" @@ -69,7 +69,7 @@ int d(int x) { D y[10]; return [x,y] { return y[x].x; }(); } // CHECK: ![[C_FUNC:.*]] = distinct !DISubprogram(name: "c"{{.*}}, line: [[C_LINE:[0-9]+]]{{.*}}, isDefinition: true // Back to C. -- 55 -// CHECK: ![[LAM_C:.*]] = !DICompositeType(tag: DW_TAG_class_type{{.*}}, scope: ![[C_FUNC]]{{.*}}, line: [[C_LINE]], +// CHECK: ![[LAM_C:.*]] = distinct !DICompositeType(tag: DW_TAG_class_type{{.*}}, scope: ![[C_FUNC]]{{.*}}, line: [[C_LINE]], // CHECK-SAME: elements: ![[LAM_C_ARGS:[0-9]+]] // CHECK: ![[LAM_C_ARGS]] = !{![[CAP_C:[0-9]+]], ![[CON_LAM_C:[0-9]+]]} // CHECK: ![[CAP_C]] = !DIDerivedType(tag: DW_TAG_member, name: "x" @@ -86,7 +86,7 @@ int d(int x) { D y[10]; return [x,y] { return y[x].x; }(); } // CHECK: ![[D_FUNC:.*]] = distinct !DISubprogram(name: "d"{{.*}}, line: [[D_LINE:[0-9]+]]{{.*}}, isDefinition: true // Back to D. -- 24 -// CHECK: ![[LAM_D:.*]] = !DICompositeType(tag: DW_TAG_class_type{{.*}}, scope: ![[D_FUNC]]{{.*}}, line: [[D_LINE]], +// CHECK: ![[LAM_D:.*]] = distinct !DICompositeType(tag: DW_TAG_class_type{{.*}}, scope: ![[D_FUNC]]{{.*}}, line: [[D_LINE]], // CHECK-SAME: elements: ![[LAM_D_ARGS:[0-9]+]] // CHECK: ![[LAM_D_ARGS]] = !{![[CAP_D_X:[0-9]+]], ![[CAP_D_Y:[0-9]+]], ![[CON_LAM_D:[0-9]+]]} // CHECK: ![[CAP_D_X]] = !DIDerivedType(tag: DW_TAG_member, name: "x" diff --git a/test/Modules/ExtDebugInfo.cpp b/test/Modules/ExtDebugInfo.cpp index 3b4547a87e0..6c38e7f464a 100644 --- a/test/Modules/ExtDebugInfo.cpp +++ b/test/Modules/ExtDebugInfo.cpp @@ -91,11 +91,11 @@ void foo() { // CHECK: !DIGlobalVariable(name: "GlobalUnion", // CHECK-SAME: type: ![[GLOBAL_UNION:[0-9]+]] -// CHECK: ![[GLOBAL_UNION]] = !DICompositeType(tag: DW_TAG_union_type, +// CHECK: ![[GLOBAL_UNION]] = distinct !DICompositeType(tag: DW_TAG_union_type, // CHECK-SAME: elements: !{{[0-9]+}}) // CHECK: !DIGlobalVariable(name: "GlobalStruct", // CHECK-SAME: type: ![[GLOBAL_STRUCT:[0-9]+]] -// CHECK: ![[GLOBAL_STRUCT]] = !DICompositeType(tag: DW_TAG_structure_type, +// CHECK: ![[GLOBAL_STRUCT]] = distinct !DICompositeType(tag: DW_TAG_structure_type, // CHECK-SAME: elements: !{{[0-9]+}}) // CHECK: !DIGlobalVariable(name: "anon", diff --git a/test/Modules/ExtDebugInfo.m b/test/Modules/ExtDebugInfo.m index d758bfda831..3b907ac729a 100644 --- a/test/Modules/ExtDebugInfo.m +++ b/test/Modules/ExtDebugInfo.m @@ -32,12 +32,12 @@ int foo(ObjCClass *c) { // CHECK: !DIGlobalVariable(name: "GlobalUnion", // CHECK-SAME: type: ![[GLOBAL_UNION:[0-9]+]] // CHECK: ![[MOD:.*]] = !DIModule(scope: null, name: "DebugObjC -// CHECK: ![[GLOBAL_UNION]] = !DICompositeType(tag: DW_TAG_union_type, +// CHECK: ![[GLOBAL_UNION]] = distinct !DICompositeType(tag: DW_TAG_union_type, // CHECK-SAME: elements: !{{[0-9]+}}) // CHECK: !DIGlobalVariable(name: "GlobalStruct", // CHECK-SAME: type: ![[GLOBAL_STRUCT:[0-9]+]] -// CHECK: ![[GLOBAL_STRUCT]] = !DICompositeType(tag: DW_TAG_structure_type, +// CHECK: ![[GLOBAL_STRUCT]] = distinct !DICompositeType(tag: DW_TAG_structure_type, // CHECK-SAME: elements: !{{[0-9]+}}) // CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "TypedefUnion", diff --git a/test/Modules/ModuleDebugInfo.m b/test/Modules/ModuleDebugInfo.m index 873f197522a..cd01e920bbc 100644 --- a/test/Modules/ModuleDebugInfo.m +++ b/test/Modules/ModuleDebugInfo.m @@ -49,7 +49,7 @@ // CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "FwdDeclared" // CHECK-SAME: elements: -// CHECK: ![[TD_UNION:.*]] = !DICompositeType(tag: DW_TAG_union_type, +// CHECK: ![[TD_UNION:.*]] = distinct !DICompositeType(tag: DW_TAG_union_type, // CHECK-SAME-NOT: name: // CHECK-SAME: elements: @@ -59,7 +59,7 @@ // CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "TypedefEnum", // CHECK-SAME: baseType: ![[TD_ENUM:.*]]) -// CHECK: ![[TD_STRUCT:.*]] = !DICompositeType(tag: DW_TAG_structure_type, +// CHECK: ![[TD_STRUCT:.*]] = distinct !DICompositeType(tag: DW_TAG_structure_type, // CHECK-SAME-NOT: name: // CHECK-SAME: elements: // CHECK: !DIDerivedType(tag: DW_TAG_typedef, name: "TypedefStruct", From 45f46dafe2645b103a4d8639cfd3dbe0e8586b4e Mon Sep 17 00:00:00 2001 From: Reid Kleckner Date: Mon, 4 Apr 2016 23:14:14 +0000 Subject: [PATCH 506/742] Fix test failure from r265361 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265362 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 308b08568e90b94b2887cee64e97085cbb4eaa87) --- test/CodeGen/align_value.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/CodeGen/align_value.cpp b/test/CodeGen/align_value.cpp index 6d0e48128cb..1601e3dcc57 100644 --- a/test/CodeGen/align_value.cpp +++ b/test/CodeGen/align_value.cpp @@ -4,7 +4,7 @@ typedef double * __attribute__((align_value(64))) aligned_double; void foo(aligned_double x, double * y __attribute__((align_value(32))), double & z __attribute__((align_value(128)))) { }; -// CHECK: define void @_Z3fooPdS_Rd(double* align 64 %x, double* align 32 %y, double* dereferenceable(8) align 128 %z) +// CHECK: define void @_Z3fooPdS_Rd(double* align 64 %x, double* align 32 %y, double* align 128 dereferenceable(8) %z) struct ad_struct { aligned_double a; From ca315dbe90004599965bed6897fe2d79c87033e6 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Mon, 18 Apr 2016 23:48:16 +0000 Subject: [PATCH 507/742] [ObjC++] Fix crash when emitting debug info for a block member capturing this. rdar://problem/23871824 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@266698 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit be9791a27874ad759d1cf53e5a4a49cde1f390eb) --- lib/CodeGen/CGDebugInfo.cpp | 11 ++++++++--- .../debug-info-block-capture-this.mm | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 test/CodeGenObjCXX/debug-info-block-capture-this.mm diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index 23735294ad4..80ce1505008 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -3285,9 +3285,14 @@ void CGDebugInfo::EmitDeclareOfBlockLiteralArgVariable(const CGBlockInfo &block, // If we have a null capture, this must be the C++ 'this' capture. if (!capture) { - const CXXMethodDecl *method = - cast(blockDecl->getNonClosureContext()); - QualType type = method->getThisType(C); + QualType type; + if (auto *Method = + cast_or_null(blockDecl->getNonClosureContext())) + type = Method->getThisType(C); + else if (auto *RDecl = dyn_cast(blockDecl->getParent())) + type = QualType(RDecl->getTypeForDecl(), 0); + else + llvm_unreachable("unexpected block declcontext"); fields.push_back(createFieldType("this", type, 0, loc, AS_public, offsetInBits, tunit, tunit)); diff --git a/test/CodeGenObjCXX/debug-info-block-capture-this.mm b/test/CodeGenObjCXX/debug-info-block-capture-this.mm new file mode 100644 index 00000000000..a1b120608cd --- /dev/null +++ b/test/CodeGenObjCXX/debug-info-block-capture-this.mm @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin -std=c++14 -fblocks -debug-info-kind=standalone -emit-llvm %s -o - | FileCheck %s +struct test +{ + int func() { return 1; } + int (^block)() = ^{ return func(); }; +}; + +int main(int argc, const char * argv[]) { + test t; + return t.block(); +} + +// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "__block_literal_1", +// CHECK-SAME: elements: ![[ELEMS:.*]]) +// CHECK: ![[ELEMS]] = !{{{.*}}, ![[THIS:[0-9]+]]} +// CHECK: ![[THIS]] = !DIDerivedType(tag: DW_TAG_member, name: "this", +// CHECK-SAME: baseType: !"_ZTS4test", + + From a0c5a84fab1215b69045b1725b7ecab63e96dc38 Mon Sep 17 00:00:00 2001 From: Vassil Vassilev Date: Wed, 16 Mar 2016 11:17:04 +0000 Subject: [PATCH 508/742] [modules] Fix adding a templated friend functions to a namespace from another module. When clang adds argument dependent lookup candidates, it can perform template instantiation. For example, it can instantiate a templated friend function and register it in the enclosing namespace's lookup table. Fixes https://llvm.org/bugs/show_bug.cgi?id=24954 Reviewed by Richard Smith. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263634 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 64e5a9bb786dabb6728d100712e8cf4d0a0bc5e1) --- lib/Serialization/ASTWriter.cpp | 12 ++++++-- test/Modules/Inputs/PR24954/A.h | 10 +++++++ test/Modules/Inputs/PR24954/B.h | 30 ++++++++++++++++++++ test/Modules/Inputs/PR24954/module.modulemap | 9 ++++++ test/Modules/pr24954.cpp | 7 +++++ 5 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 test/Modules/Inputs/PR24954/A.h create mode 100644 test/Modules/Inputs/PR24954/B.h create mode 100644 test/Modules/Inputs/PR24954/module.modulemap create mode 100644 test/Modules/pr24954.cpp diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index b2da67d997a..0497213cc94 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -5703,8 +5703,16 @@ static bool isImportedDeclContext(ASTReader *Chain, const Decl *D) { } void ASTWriter::AddedVisibleDecl(const DeclContext *DC, const Decl *D) { - // TU and namespaces are handled elsewhere. - if (isa(DC) || isa(DC)) + // TU is handled elsewhere. + if (isa(DC)) + return; + + // Namespaces are handled elsewhere, except for template instantiations of + // FunctionTemplateDecls in namespaces. We are interested in cases where the + // local instantiations are added to an imported context. Only happens when + // adding ADL lookup candidates, for example templated friends. + if (isa(DC) && D->getFriendObjectKind() == Decl::FOK_None && + !isa(D)) return; // We're only interested in cases where a local declaration is added to an diff --git a/test/Modules/Inputs/PR24954/A.h b/test/Modules/Inputs/PR24954/A.h new file mode 100644 index 00000000000..5e5d5bf92ce --- /dev/null +++ b/test/Modules/Inputs/PR24954/A.h @@ -0,0 +1,10 @@ +#include "B.h" + +template +class Expr { +public: + void print(B::basic_ostream& os) { + os << B::setw(42); + os << B::endl; + } +}; diff --git a/test/Modules/Inputs/PR24954/B.h b/test/Modules/Inputs/PR24954/B.h new file mode 100644 index 00000000000..a8ddc718927 --- /dev/null +++ b/test/Modules/Inputs/PR24954/B.h @@ -0,0 +1,30 @@ +namespace B { + + template + struct basic_ostream { + basic_ostream& operator<<(basic_ostream& (*__pf)()); + }; + + + template basic_ostream<_CharT>& + endl(); + + struct S1 { + template friend void + operator<<(basic_ostream<_CharT>& __os, const S1& __x); + }; + + S1 setw(int __n); + + template class S2; + + template void + operator<<(basic_ostream<_CharT>& __os, const S2<_CharT>& __x); + + template + struct S2 { + template friend void + operator<<(basic_ostream<_Cp>& __os, const S2<_Cp>& __x); + }; + +} diff --git a/test/Modules/Inputs/PR24954/module.modulemap b/test/Modules/Inputs/PR24954/module.modulemap new file mode 100644 index 00000000000..49374181d75 --- /dev/null +++ b/test/Modules/Inputs/PR24954/module.modulemap @@ -0,0 +1,9 @@ +module A { + header "A.h" + export * +} + +module B { + header "B.h" + export * +} diff --git a/test/Modules/pr24954.cpp b/test/Modules/pr24954.cpp new file mode 100644 index 00000000000..407ee06e402 --- /dev/null +++ b/test/Modules/pr24954.cpp @@ -0,0 +1,7 @@ +// RUN: rm -rf %t +// RUN: %clang_cc1 -I%S/Inputs/PR24954 -verify %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -I%S/Inputs/PR24954 -verify %s + +#include "A.h" + +// expected-no-diagnostics From 2ee8fa69ae8e5a3980b2c76c6ae04c4ab0df904a Mon Sep 17 00:00:00 2001 From: Vassil Vassilev Date: Wed, 6 Apr 2016 20:56:03 +0000 Subject: [PATCH 509/742] [modules] Don't try to add lookup results to non-lookup contexts. Fixes https://llvm.org/bugs/show_bug.cgi?id=27186 Patch reviewed by Richard Smith. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265597 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 8bb4ef4de38407713d2f906aa82fdb8913d2c0e4) --- lib/AST/DeclBase.cpp | 7 +++++-- lib/Serialization/ASTWriter.cpp | 3 +++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index 6cfe4d5b185..e9fc0f095ee 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -1556,9 +1556,12 @@ void DeclContext::makeDeclVisibleInContextWithFlags(NamedDecl *D, bool Internal, bool Recoverable) { assert(this == getPrimaryContext() && "expected a primary DC"); - // Skip declarations within functions. - if (isFunctionOrMethod()) + if (!isLookupContext()) { + if (isTransparentContext()) + getParent()->getPrimaryContext() + ->makeDeclVisibleInContextWithFlags(D, Internal, Recoverable); return; + } // Skip declarations which should be invisible to name lookup. if (shouldBeHidden(D)) diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 0497213cc94..462093b125d 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -5703,6 +5703,9 @@ static bool isImportedDeclContext(ASTReader *Chain, const Decl *D) { } void ASTWriter::AddedVisibleDecl(const DeclContext *DC, const Decl *D) { + assert(DC->isLookupContext() && + "Should not add lookup results to non-lookup contexts!"); + // TU is handled elsewhere. if (isa(DC)) return; From 0cdea23a3696e26efb72012355e57e6bb4562eae Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Tue, 19 Apr 2016 20:31:19 +0000 Subject: [PATCH 510/742] cc1as: Don't crash when CIE is requested and no DWARF version is specified. This patch changes the default DWARF version for cc1as from invalid 0 to 2, which should be the lowest common denominator on all platforms. rdar://problem/24735813 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@266814 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 99fb8a4a1fbb18b0031f22e5d8aa320a37c0acbb) --- test/Driver/as-dwarf-cie.s | 37 +++++++++++++++++++++++++++++++++++++ tools/driver/cc1as_main.cpp | 2 +- 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 test/Driver/as-dwarf-cie.s diff --git a/test/Driver/as-dwarf-cie.s b/test/Driver/as-dwarf-cie.s new file mode 100644 index 00000000000..73d987afd42 --- /dev/null +++ b/test/Driver/as-dwarf-cie.s @@ -0,0 +1,37 @@ +# REQUIRES: x86-registered-target +# Test that there is a sane default CIE version. +# RUN: %clang -cc1as -triple i386-apple-darwin -filetype obj %s -o %t +# RUN: llvm-objdump -dwarf=frames %t | FileCheck %s +# CHECK: .debug_frame contents: +# CHECK: CIE +# CHECK: Version: 1 + .section __TEXT,__text,regular,pure_instructions + .globl _f + .p2align 4, 0x90 +_f: ## @f +Lfunc_begin0: + .file 1 "test.c" + .loc 1 1 0 ## test.c:1:0 + .cfi_startproc +## BB#0: ## %entry + pushl %ebp +Ltmp0: + .cfi_def_cfa_offset 8 +Ltmp1: + .cfi_offset %ebp, -8 + movl %esp, %ebp +Ltmp2: + .cfi_def_cfa_register %ebp +Ltmp3: + .loc 1 1 11 prologue_end ## test.c:1:11 + popl %ebp + retl +Ltmp4: +Lfunc_end0: + .cfi_endproc + .cfi_sections .debug_frame + +.subsections_via_symbols + .section __DWARF,__debug_line,regular,debug +Lsection_line: +Lline_table_start0: diff --git a/tools/driver/cc1as_main.cpp b/tools/driver/cc1as_main.cpp index 388a7693584..bcc2cde17a8 100644 --- a/tools/driver/cc1as_main.cpp +++ b/tools/driver/cc1as_main.cpp @@ -200,7 +200,7 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts, // Any DebugInfoKind implies GenDwarfForAssembly. Opts.GenDwarfForAssembly = Args.hasArg(OPT_debug_info_kind_EQ); Opts.CompressDebugSections = Args.hasArg(OPT_compress_debug_sections); - Opts.DwarfVersion = getLastArgIntValue(Args, OPT_dwarf_version_EQ, 0, Diags); + Opts.DwarfVersion = getLastArgIntValue(Args, OPT_dwarf_version_EQ, 2, Diags); Opts.DwarfDebugFlags = Args.getLastArgValue(OPT_dwarf_debug_flags); Opts.DwarfDebugProducer = Args.getLastArgValue(OPT_dwarf_debug_producer); Opts.DebugCompilationDir = Args.getLastArgValue(OPT_fdebug_compilation_dir); From 74933f775d6d94f96748dbee14fb6452461e45b1 Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Wed, 20 Apr 2016 09:41:48 -0700 Subject: [PATCH 511/742] [swift_newtype] Support for swift_wrapper/newtype attribute. Adds in compiler support, and tests. --- include/clang/Basic/Attr.td | 9 +++++ include/clang/Basic/AttrDocs.td | 10 ++++++ include/clang/Basic/DiagnosticSemaKinds.td | 3 ++ include/clang/Parse/Parser.h | 8 +++++ lib/Parse/ParseDecl.cpp | 36 +++++++++++++++++++ lib/Sema/SemaDeclAttr.cpp | 42 ++++++++++++++++++++++ test/SemaObjC/attr-swift_newtype.c | 20 +++++++++++ 7 files changed, 128 insertions(+) create mode 100644 test/SemaObjC/attr-swift_newtype.c diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 7adfdc4c107..5d3509402f5 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -1347,6 +1347,15 @@ def SwiftName : InheritableAttr { let Documentation = [Undocumented]; } +def SwiftNewtype : Attr { + let Spellings = [GNU<"swift_newtype">, GNU<"swift_wrapper">]; + let Subjects = SubjectList<[TypedefName], ErrorDiag, "ExpectedType">; + let Args = [EnumArgument<"NewtypeKind", "NewtypeKind", + ["struct", "enum"], + ["NK_Struct", "NK_Enum"]>]; + let Documentation = [SwiftNewtypeDocs]; +} + def SwiftPrivate : InheritableAttr { let Spellings = [GCC<"swift_private">]; let Documentation = [Undocumented]; diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td index 24d212967a8..2132ea3f28c 100644 --- a/include/clang/Basic/AttrDocs.td +++ b/include/clang/Basic/AttrDocs.td @@ -1807,6 +1807,16 @@ All of these conventions except ``none`` require the function to have an error p }]; } +def SwiftNewtypeDocs : Documentation { + let Category = SwiftDocs; + let Content = [{ +The ``swift_newtype`` attribute indicates that the typedef to which the attribute appertains is imported as a new Swift type of the typedef's name. +* ``swift_newtype(struct)`` means that a Swift struct will be created for this typedef. +* ``swift_newtype(enum)`` means that a Swift enum will be created for this typedef. + }]; +} + + def NotTailCalledDocs : Documentation { let Category = DocCatFunction; let Content = [{ diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 765e6368f4a..798ac61c75e 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -3024,6 +3024,9 @@ def err_attr_swift_error_no_error_parameter : Error< def err_attr_swift_error_return_type : Error< "%0 attribute with '%1' convention can only be applied to a " "%select{function|method}2 returning %select{an integral type|a pointer}3">; +def warn_swift_newtype_attribute_non_typedef : Warning< + "'swift_newtype' attribute may be put on a typedef only; " + "attribute is ignored">; // Function Parameter Semantic Analysis. def err_param_with_void_type : Error<"argument may not have 'void' type">; diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 00885a5c710..d52db90ac6f 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -2224,6 +2224,14 @@ class Parser : public CodeCompletionHandler { SourceLocation ScopeLoc, AttributeList::Syntax Syntax); + void ParseSwiftNewtypeAttribute(IdentifierInfo &SwiftNewtype, + SourceLocation SwiftNewtypeLoc, + ParsedAttributes &attrs, + SourceLocation *endLoc, + IdentifierInfo *ScopeName, + SourceLocation ScopeLoc, + AttributeList::Syntax Syntax); + void ParseAttributeWithTypeArg(IdentifierInfo &AttrName, SourceLocation AttrNameLoc, ParsedAttributes &Attrs, diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 7b7adac2b75..f0f446730d6 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -238,6 +238,38 @@ IdentifierLoc *Parser::ParseIdentifierLoc() { return IL; } +void Parser::ParseSwiftNewtypeAttribute( + IdentifierInfo &SwiftNewtype, SourceLocation SwiftNewtypeLoc, + ParsedAttributes &attrs, SourceLocation *endLoc, IdentifierInfo *ScopeName, + SourceLocation ScopeLoc, AttributeList::Syntax Syntax) { + + BalancedDelimiterTracker Parens(*this, tok::l_paren); + Parens.consumeOpen(); + + if (Tok.is(tok::r_paren)) { + Diag(Tok.getLocation(), diag::err_argument_required_after_attribute); + Parens.consumeClose(); + return; + } + if (Tok.isNot(tok::kw_struct) && Tok.isNot(tok::kw_enum)) { + Diag(Tok.getLocation(), diag::warn_attribute_type_not_supported) + << &SwiftNewtype << Tok.getIdentifierInfo(); + if (!isTokenSpecial()) + ConsumeToken(); + Parens.consumeClose(); + return; + } + auto IL = IdentifierLoc::create(Actions.Context, Tok.getLocation(), + Tok.getIdentifierInfo()); + ConsumeToken(); + auto identLoc = ArgsUnion(IL); + + attrs.addNew(&SwiftNewtype, + SourceRange(SwiftNewtypeLoc, Parens.getCloseLocation()), + ScopeName, ScopeLoc, &identLoc, 1, Syntax); + Parens.consumeClose(); +} + void Parser::ParseAttributeWithTypeArg(IdentifierInfo &AttrName, SourceLocation AttrNameLoc, ParsedAttributes &Attrs, @@ -357,6 +389,10 @@ void Parser::ParseGNUAttributeArgs(IdentifierInfo *AttrName, ParseTypeTagForDatatypeAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName, ScopeLoc, Syntax); return; + } else if (AttrKind == AttributeList::AT_SwiftNewtype) { + ParseSwiftNewtypeAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc, + ScopeName, ScopeLoc, Syntax); + return; } else if (attributeIsTypeArgAttr(*AttrName)) { ParseAttributeWithTypeArg(*AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName, ScopeLoc, Syntax); diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index ecc744c44b8..af701278ea7 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -4862,6 +4862,45 @@ static void handleSwiftBridgeAttr(Sema &S, Decl *D, const AttributeList &Attr) { Attr.getAttributeSpellingListIndex())); } +static void handleSwiftNewtypeAttr(Sema &S, Decl *D, const AttributeList &Attr) { + // Make sure that there is an identifier as the annotation's single + // argument. + if (Attr.getNumArgs() != 1) { + S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) + << Attr.getName() << 1; + Attr.setInvalid(); + return; + } + if (!Attr.isArgIdent(0)) { + S.Diag(Attr.getLoc(), diag::err_attribute_argument_type) + << Attr.getName() << AANT_ArgumentIdentifier; + Attr.setInvalid(); + return; + } + + IdentifierInfo *II = Attr.getArgAsIdent(0)->Ident; + SwiftNewtypeAttr::NewtypeKind Kind; + if (II->isStr("struct")) + Kind = SwiftNewtypeAttr::NK_Struct; + else if (II->isStr("enum")) + Kind = SwiftNewtypeAttr::NK_Enum; + else { + S.Diag(Attr.getLoc(), diag::warn_attribute_type_not_supported) + << Attr.getName() << II; + Attr.setInvalid(); + return; + } + + if (!isa(D)) { + S.Diag(Attr.getLoc(), diag::warn_swift_newtype_attribute_non_typedef); + return; + } + + D->addAttr(::new (S.Context) + SwiftNewtypeAttr(Attr.getRange(), S.Context, Kind, + Attr.getAttributeSpellingListIndex())); +} + //===----------------------------------------------------------------------===// // Microsoft specific attribute handlers. //===----------------------------------------------------------------------===// @@ -6078,6 +6117,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_SwiftBridge: handleSwiftBridgeAttr(S, D, Attr); break; + case AttributeList::AT_SwiftNewtype: + handleSwiftNewtypeAttr(S, D, Attr); + break; } } diff --git a/test/SemaObjC/attr-swift_newtype.c b/test/SemaObjC/attr-swift_newtype.c new file mode 100644 index 00000000000..61e4d89642a --- /dev/null +++ b/test/SemaObjC/attr-swift_newtype.c @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -verify -fsyntax-only %s + +typedef int T1 __attribute__((swift_newtype(struct))); +typedef int T2 __attribute__((swift_newtype(enum))); + +typedef int T3 __attribute__((swift_wrapper(struct))); +typedef int T4 __attribute__((swift_wrapper(enum))); + + +typedef int Bad1 __attribute__((swift_newtype(bad))); // expected-warning{{'swift_newtype' attribute argument not supported: 'bad'}} +typedef int Bad2 __attribute__((swift_newtype())); // expected-error{{argument required after attribute}} +typedef int Bad3 __attribute__((swift_newtype(bad, badder))); + // expected-error@-1{{expected ')'}} + // expected-note@-2{{to match this '('}} + // expected-warning@-3{{'swift_newtype' attribute argument not supported: 'bad'}} + + +// TODO: better error message below +// FIXME: why is this a parse error, rather than Sema error triggering? +struct Bad4 __attribute__((swift_newtype(struct))) { }; // expected-error{{expected identifier or '('}} From fbe65cfab684a34b11208641bfdee9bbb9093b31 Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Wed, 20 Apr 2016 12:47:18 -0700 Subject: [PATCH 512/742] [swift_name] add warning group, to enable flags --- include/clang/Basic/DiagnosticSemaKinds.td | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 798ac61c75e..edb3041800b 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -3026,7 +3026,7 @@ def err_attr_swift_error_return_type : Error< "%select{function|method}2 returning %select{an integral type|a pointer}3">; def warn_swift_newtype_attribute_non_typedef : Warning< "'swift_newtype' attribute may be put on a typedef only; " - "attribute is ignored">; + "attribute is ignored">, InGroup>; // Function Parameter Semantic Analysis. def err_param_with_void_type : Error<"argument may not have 'void' type">; From 313fd69174c36a43d2d8302d4816cbc54d73875b Mon Sep 17 00:00:00 2001 From: Manuel Klimek Date: Tue, 1 Mar 2016 10:56:19 +0000 Subject: [PATCH 513/742] Optionally demote fatal errors to non-fatal errors. This behavior is enabled when the new CXTranslationUnit_KeepGoing option is passed to clang_parseTranslationUnit{,2}. It is geared towards use by IDEs and similar consumers of the clang-c API where fatal errors may arise when parsing incomplete code mid-edit, or when include paths are not properly configured yet. In such situations one still wants to get as much information as possible about a TU. Previously, the semantic analysis would not instantiate templates or report additional fatal errors after the first fatal error was encountered. Fixes PR24268. Patch by Milian Wolff. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262318 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit ac0a11ae22d93e452b579b2adf6af933c49f9616) --- include/clang-c/Index.h | 13 ++++++++++++- include/clang/Basic/Diagnostic.h | 7 +++++++ lib/Basic/Diagnostic.cpp | 1 + lib/Basic/DiagnosticIDs.cpp | 6 ++++++ test/Index/keep-going.cpp | 29 +++++++++++++++++++++++++++++ tools/c-index-test/c-index-test.c | 2 ++ tools/libclang/CIndex.cpp | 3 +++ unittests/Basic/DiagnosticTest.cpp | 23 +++++++++++++++++++++++ 8 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 test/Index/keep-going.cpp diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h index 28ec30ccba5..069cd04f2d9 100644 --- a/include/clang-c/Index.h +++ b/include/clang-c/Index.h @@ -1208,7 +1208,18 @@ enum CXTranslationUnit_Flags { * trades runtime on the first parse (serializing the preamble takes time) for * reduced runtime on the second parse (can now reuse the preamble). */ - CXTranslationUnit_CreatePreambleOnFirstParse = 0x100 + CXTranslationUnit_CreatePreambleOnFirstParse = 0x100, + + /** + * \brief Do not stop processing when fatal errors are encountered. + * + * When fatal errors are encountered while parsing a translation unit, + * semantic analysis is typically stopped early when compiling code. A common + * source for fatal errors are unresolvable include files. For the + * purposes of an IDE, this is undesirable behavior and as much information + * as possible should be reported. Use this flag to enable this behavior. + */ + CXTranslationUnit_KeepGoing = 0x200 }; /** diff --git a/include/clang/Basic/Diagnostic.h b/include/clang/Basic/Diagnostic.h index 1d6c19bc953..dd4443e5dd1 100644 --- a/include/clang/Basic/Diagnostic.h +++ b/include/clang/Basic/Diagnostic.h @@ -173,6 +173,7 @@ class DiagnosticsEngine : public RefCountedBase { bool WarningsAsErrors; // Treat warnings like errors. bool EnableAllWarnings; // Enable all warnings. bool ErrorsAsFatal; // Treat errors like fatal errors. + bool FatalsAsError; // Treat fatal errors like errors. bool SuppressSystemWarnings; // Suppress warnings in system headers. bool SuppressAllDiagnostics; // Suppress all diagnostics. bool ElideType; // Elide common types of templates. @@ -455,6 +456,12 @@ class DiagnosticsEngine : public RefCountedBase { void setErrorsAsFatal(bool Val) { ErrorsAsFatal = Val; } bool getErrorsAsFatal() const { return ErrorsAsFatal; } + /// \brief When set to true, any fatal error reported is made an error. + /// + /// This setting takes precedence over the setErrorsAsFatal setting above. + void setFatalsAsError(bool Val) { FatalsAsError = Val; } + bool getFatalsAsError() const { return FatalsAsError; } + /// \brief When set to true mask warnings that come from system headers. void setSuppressSystemWarnings(bool Val) { SuppressSystemWarnings = Val; } bool getSuppressSystemWarnings() const { return SuppressSystemWarnings; } diff --git a/lib/Basic/Diagnostic.cpp b/lib/Basic/Diagnostic.cpp index 7cf7305827f..f10d156743b 100644 --- a/lib/Basic/Diagnostic.cpp +++ b/lib/Basic/Diagnostic.cpp @@ -68,6 +68,7 @@ DiagnosticsEngine::DiagnosticsEngine( WarningsAsErrors = false; EnableAllWarnings = false; ErrorsAsFatal = false; + FatalsAsError = false; SuppressSystemWarnings = false; SuppressAllDiagnostics = false; ElideType = true; diff --git a/lib/Basic/DiagnosticIDs.cpp b/lib/Basic/DiagnosticIDs.cpp index a34c7fecb53..efc3f94c959 100644 --- a/lib/Basic/DiagnosticIDs.cpp +++ b/lib/Basic/DiagnosticIDs.cpp @@ -462,6 +462,12 @@ DiagnosticIDs::getDiagnosticSeverity(unsigned DiagID, SourceLocation Loc, Result = diag::Severity::Fatal; } + // If explicitly requested, map fatal errors to errors. + if (Result == diag::Severity::Fatal) { + if (Diag.FatalsAsError) + Result = diag::Severity::Error; + } + // Custom diagnostics always are emitted in system headers. bool ShowInSystemHeader = !GetDiagInfo(DiagID) || GetDiagInfo(DiagID)->WarnShowInSystemHeader; diff --git a/test/Index/keep-going.cpp b/test/Index/keep-going.cpp new file mode 100644 index 00000000000..9bf23947d16 --- /dev/null +++ b/test/Index/keep-going.cpp @@ -0,0 +1,29 @@ +#include "missing1.h" + +template +class A { T a; }; + +class B : public A { }; + +#include "missing2.h" + +class C : public A { }; + +// RUN: env CINDEXTEST_EDITING=1 CINDEXTEST_KEEP_GOING=1 c-index-test -test-print-type %s 2> %t.stderr.txt | FileCheck %s +// RUN: FileCheck -check-prefix CHECK-DIAG %s < %t.stderr.txt + +// CHECK: inclusion directive=missing1.h ((null)) [type=] [typekind=Invalid] [isPOD=0] +// CHECK: inclusion directive=missing2.h ((null)) [type=] [typekind=Invalid] [isPOD=0] +// CHECK: ClassTemplate=A:4:7 (Definition) [type=] [typekind=Invalid] [isPOD=0] +// CHECK: TemplateTypeParameter=T:3:16 (Definition) [type=T] [typekind=Unexposed] [canonicaltype=type-parameter-0-0] [canonicaltypekind=Unexposed] [isPOD=0] +// CHECK: FieldDecl=a:4:13 (Definition) [type=T] [typekind=Unexposed] [canonicaltype=type-parameter-0-0] [canonicaltypekind=Unexposed] [isPOD=0] +// CHECK: TypeRef=T:3:16 [type=T] [typekind=Unexposed] [canonicaltype=type-parameter-0-0] [canonicaltypekind=Unexposed] [isPOD=0] +// CHECK: ClassDecl=B:6:7 (Definition) [type=B] [typekind=Record] [isPOD=0] +// CHECK: C++ base class specifier=A:4:7 [access=public isVirtual=false] [type=A] [typekind=Unexposed] [canonicaltype=A] [canonicaltypekind=Record] [templateargs/1= [type=int] [typekind=Int]] [isPOD=0] [nbFields=1] +// CHECK: TemplateRef=A:4:7 [type=] [typekind=Invalid] [isPOD=0] +// CHECK: ClassDecl=C:10:7 (Definition) [type=C] [typekind=Record] [isPOD=0] +// CHECK: C++ base class specifier=A:4:7 [access=public isVirtual=false] [type=A] [typekind=Unexposed] [canonicaltype=A] [canonicaltypekind=Record] [templateargs/1= [type=float] [typekind=Float]] [isPOD=0] [nbFields=1] +// CHECK: TemplateRef=A:4:7 [type=] [typekind=Invalid] [isPOD=0] + +// CHECK-DIAG: keep-going.cpp:1:10: error: 'missing1.h' file not found +// CHECK-DIAG: keep-going.cpp:8:10: error: 'missing2.h' file not found diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c index a67afb7cfa8..802ecec4fc8 100644 --- a/tools/c-index-test/c-index-test.c +++ b/tools/c-index-test/c-index-test.c @@ -80,6 +80,8 @@ static unsigned getDefaultParsingOptions() { options |= CXTranslationUnit_IncludeBriefCommentsInCodeCompletion; if (getenv("CINDEXTEST_CREATE_PREAMBLE_ON_FIRST_PARSE")) options |= CXTranslationUnit_CreatePreambleOnFirstParse; + if (getenv("CINDEXTEST_KEEP_GOING")) + options |= CXTranslationUnit_KeepGoing; return options; } diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index 49ae3ae3b2d..57664d02805 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -3110,6 +3110,9 @@ clang_parseTranslationUnit_Impl(CXIndex CIdx, const char *source_filename, IntrusiveRefCntPtr Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions)); + if (options & CXTranslationUnit_KeepGoing) + Diags->setFatalsAsError(true); + // Recover resources if we crash before exiting this function. llvm::CrashRecoveryContextCleanupRegistrar > diff --git a/unittests/Basic/DiagnosticTest.cpp b/unittests/Basic/DiagnosticTest.cpp index fa2b56e0834..4ffa0837bd6 100644 --- a/unittests/Basic/DiagnosticTest.cpp +++ b/unittests/Basic/DiagnosticTest.cpp @@ -46,4 +46,27 @@ TEST(DiagnosticTest, suppressAndTrap) { EXPECT_FALSE(Diags.hasUnrecoverableErrorOccurred()); } +// Check that FatalsAsErrors works as intended +TEST(DiagnosticTest, fatalsAsErrors) { + DiagnosticsEngine Diags(new DiagnosticIDs(), + new DiagnosticOptions, + new IgnoringDiagConsumer()); + Diags.setFatalsAsError(true); + + // Diag that would set UncompilableErrorOccurred and ErrorOccurred. + Diags.Report(diag::err_target_unknown_triple) << "unknown"; + + // Diag that would set UnrecoverableErrorOccurred and ErrorOccurred. + Diags.Report(diag::err_cannot_open_file) << "file" << "error"; + + // Diag that would set FatalErrorOccurred + // (via non-note following a fatal error). + Diags.Report(diag::warn_mt_message) << "warning"; + + EXPECT_TRUE(Diags.hasErrorOccurred()); + EXPECT_FALSE(Diags.hasFatalErrorOccurred()); + EXPECT_TRUE(Diags.hasUncompilableErrorOccurred()); + EXPECT_TRUE(Diags.hasUnrecoverableErrorOccurred()); +} + } From 3c21fde0671f102d0ce5270f083ba68180e0e7c5 Mon Sep 17 00:00:00 2001 From: Filipe Cabecinhas Date: Tue, 1 Mar 2016 15:33:52 +0000 Subject: [PATCH 514/742] [cmake] Add a few more compiler-rt check-* targets for EXTERNAL_COMPILER_RT git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262341 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 438e6a9a9f517f6b5566c9b597cdaeafb2772811) --- runtime/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 4775b0d25da..f23bd0ab88f 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -115,7 +115,8 @@ if(LLVM_BUILD_EXTERNAL_COMPILER_RT AND EXISTS ${COMPILER_RT_SRC_ROOT}/) # Add top-level targets for various compiler-rt test suites. set(COMPILER_RT_TEST_SUITES check-asan check-asan-dynamic check-dfsan - check-lsan check-msan check-sanitizer check-tsan check-ubsan) + check-lsan check-msan check-sanitizer check-tsan check-ubsan + check-profile check-cfi check-cfi-and-supported check-safestack) foreach(test_suite ${COMPILER_RT_TEST_SUITES}) get_ext_project_build_command(run_test_suite ${test_suite}) add_custom_target(${test_suite} From 2f8be4e30efcd7fd01433f8173f674f85f7cd6d0 Mon Sep 17 00:00:00 2001 From: David Majnemer Date: Wed, 2 Mar 2016 06:48:47 +0000 Subject: [PATCH 515/742] [Sema] PR26444 fix crash when alignment value is >= 2**16 Sema allows max values up to 2**28, use unsigned instead of unsiged short to hold values that large. Differential Revision: http://reviews.llvm.org/D17248 Patch by Don Hinton! git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262466 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 1684c47142a9ee2e4fa55c720a0180704ba81465) --- lib/CodeGen/CGValue.h | 2 +- test/Sema/attr-aligned.c | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/CodeGen/CGValue.h b/lib/CodeGen/CGValue.h index 3ccc4cda89f..53a376df645 100644 --- a/lib/CodeGen/CGValue.h +++ b/lib/CodeGen/CGValue.h @@ -445,7 +445,7 @@ class AggValueSlot { // Qualifiers Qualifiers Quals; - unsigned short Alignment; + unsigned Alignment; /// DestructedFlag - This is set to true if some external code is /// responsible for setting up a destructor for the slot. Otherwise diff --git a/test/Sema/attr-aligned.c b/test/Sema/attr-aligned.c index 0a2698ec91a..b8d2fc6863d 100644 --- a/test/Sema/attr-aligned.c +++ b/test/Sema/attr-aligned.c @@ -3,6 +3,9 @@ int x __attribute__((aligned(3))); // expected-error {{requested alignment is not a power of 2}} int y __attribute__((aligned(1 << 29))); // expected-error {{requested alignment must be 268435456 bytes or smaller}} +// PR26444 +int y __attribute__((aligned(1 << 28))); + // PR3254 short g0[3] __attribute__((aligned)); short g0_chk[__alignof__(g0) == 16 ? 1 : -1]; From f03caf50b8e99b20b223f9e8c81401a80b03ae23 Mon Sep 17 00:00:00 2001 From: George Burgess IV Date: Thu, 31 Mar 2016 00:16:25 +0000 Subject: [PATCH 516/742] [Sema] Fix PR27122: ICE with enable_if+ill-formed call. In some cases, when we encounter a direct function call with an incorrect number of arguments, we'll emit a diagnostic, and pretend that the call to the function was valid. For example, in C: int foo(); int a = foo(1); Prior to this patch, we'd get an ICE if foo had an enable_if attribute, because CheckEnableIf assumes that the number of arguments it gets passed is valid for the function it's passed. Now, we check that the number of args looks valid prior to checking enable_if conditions. This fix was not done inside of CheckEnableIf because the problem presently can only occur in one caller of CheckEnableIf (ActOnCallExpr). Additionally, checking inside of CheckEnableIf would make us emit multiple diagnostics for the same error (one "enable_if failed", one "you gave this function the wrong number of arguments"), which seems worse than just complaining about the latter. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@264975 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 7bbc253b7d872d5e7c2558ac82000ebe3a5c4de0) Conflicts: test/SemaCXX/enable_if.cpp --- lib/Sema/SemaExpr.cpp | 17 ++++++++++++++++- test/Sema/enable_if.c | 7 +++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 715449204fa..6eaa35a0ee5 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -5002,6 +5002,14 @@ static FunctionDecl *rewriteBuiltinFunctionDecl(Sema *Sema, ASTContext &Context, return OverloadDecl; } +static bool isNumberOfArgsValidForCall(Sema &S, const FunctionDecl *Callee, + std::size_t NumArgs) { + if (S.TooManyArguments(Callee->getNumParams(), NumArgs, + /*PartialOverloading=*/false)) + return Callee->isVariadic(); + return Callee->getMinRequiredArguments() <= NumArgs; +} + /// ActOnCallExpr - Handle a call to Fn with the specified array of arguments. /// This provides the location of the left/right parens and a list of comma /// locations. @@ -5133,7 +5141,14 @@ Sema::ActOnCallExpr(Scope *S, Expr *Fn, SourceLocation LParenLoc, Fn->getLocStart())) return ExprError(); - if (FD->hasAttr()) { + // CheckEnableIf assumes that the we're passing in a sane number of args for + // FD, but that doesn't always hold true here. This is because, in some + // cases, we'll emit a diag about an ill-formed function call, but then + // we'll continue on as if the function call wasn't ill-formed. So, if the + // number of args looks incorrect, don't do enable_if checks; we should've + // already emitted an error about the bad call. + if (FD->hasAttr() && + isNumberOfArgsValidForCall(*this, FD, ArgExprs.size())) { if (const EnableIfAttr *Attr = CheckEnableIf(FD, ArgExprs, true)) { Diag(Fn->getLocStart(), isa(FD) ? diff --git a/test/Sema/enable_if.c b/test/Sema/enable_if.c index 0cd9c48f42b..4034aa2bfab 100644 --- a/test/Sema/enable_if.c +++ b/test/Sema/enable_if.c @@ -142,4 +142,11 @@ void test8() { void (*p1)(int) = &f4; // expected-error{{cannot take address of function 'f4' becuase it has one or more non-tautological enable_if conditions}} void (*p2)(int) = f4; // expected-error{{cannot take address of function 'f4' becuase it has one or more non-tautological enable_if conditions}} } + +void regular_enable_if(int a) __attribute__((enable_if(a, ""))); // expected-note 3{{declared here}} +void PR27122_ext() { + regular_enable_if(0, 2); // expected-error{{too many arguments}} + regular_enable_if(1, 2); // expected-error{{too many arguments}} + regular_enable_if(); // expected-error{{too few arguments}} +} #endif From baa9be24e53adfe2f0ad88d7a4e31f63930c88fd Mon Sep 17 00:00:00 2001 From: Hans Wennborg Date: Mon, 11 Apr 2016 20:53:59 +0000 Subject: [PATCH 517/742] libclang: fix two memory leaks (PR26292) git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265994 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 634da19447e38e04accee3e70bc5a26d56b3fbbd) --- tools/libclang/CIndex.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index 57664d02805..1f862c1e6c4 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -3530,12 +3530,13 @@ static const ExprEvalResult* evaluateExpr(Expr *expr, CXCursor C) { rettype = callExpr->getCallReturnType(ctx); if (rettype->isVectorType() || callExpr->getNumArgs() > 1) { + clang_EvalResult_dispose((CXEvalResult *)result); return nullptr; } if (rettype->isIntegralType(ctx) || rettype->isRealFloatingType()) { if(callExpr->getNumArgs() == 1 && - !callExpr->getArg(0)->getType()->isIntegralType(ctx)){ - + !callExpr->getArg(0)->getType()->isIntegralType(ctx)) { + clang_EvalResult_dispose((CXEvalResult *)result); return nullptr; } } else if(rettype.getAsString() == "CFStringRef") { From bf6c74c57958ddc8c24a71d23c7e596c894e7c9c Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Tue, 19 Apr 2016 17:54:29 +0000 Subject: [PATCH 518/742] [CodeGen] Widen non-power-of-2 vector HFA base types. Currently, for the ppc64--gnu and aarch64 ABIs, we recognize: typedef __attribute__((__ext_vector_type__(3))) float v3f32; typedef __attribute__((__ext_vector_type__(16))) char v16i8; struct HFA { v3f32 a; v16i8 b; }; as an HFA. Since the first type encountered is used as the base type, we pass the HFA as: [2 x <3 x float>] Which leads to incorrect IR (relying on padding values) when the second field is used. Instead, explicitly widen the vector (after size rounding) in isHomogeneousAggregate. Differential Revision: http://reviews.llvm.org/D18998 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@266784 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit c585033ef89cc7abe89b03cc4e94528abe3ee3aa) --- lib/CodeGen/TargetInfo.cpp | 13 +++++- test/CodeGen/aarch64-arguments-hfa-v3.c | 20 +++++++++ test/CodeGen/arm64-arguments.c | 31 +++++++++++++ test/CodeGen/ppc64le-aggregates.c | 58 ++++++++++++------------- 4 files changed, 92 insertions(+), 30 deletions(-) create mode 100644 test/CodeGen/aarch64-arguments-hfa-v3.c diff --git a/lib/CodeGen/TargetInfo.cpp b/lib/CodeGen/TargetInfo.cpp index f7237b68c60..68bb6cda813 100644 --- a/lib/CodeGen/TargetInfo.cpp +++ b/lib/CodeGen/TargetInfo.cpp @@ -4030,8 +4030,19 @@ bool ABIInfo::isHomogeneousAggregate(QualType Ty, const Type *&Base, // agree in both total size and mode (float vs. vector) are // treated as being equivalent here. const Type *TyPtr = Ty.getTypePtr(); - if (!Base) + if (!Base) { Base = TyPtr; + // If it's a non-power-of-2 vector, its size is already a power-of-2, + // so make sure to widen it explicitly. + if (const VectorType *VT = Base->getAs()) { + QualType EltTy = VT->getElementType(); + unsigned NumElements = + getContext().getTypeSize(VT) / getContext().getTypeSize(EltTy); + Base = getContext() + .getVectorType(EltTy, NumElements, VT->getVectorKind()) + .getTypePtr(); + } + } if (Base->isVectorType() != TyPtr->isVectorType() || getContext().getTypeSize(Base) != getContext().getTypeSize(TyPtr)) diff --git a/test/CodeGen/aarch64-arguments-hfa-v3.c b/test/CodeGen/aarch64-arguments-hfa-v3.c new file mode 100644 index 00000000000..59fa5e959ed --- /dev/null +++ b/test/CodeGen/aarch64-arguments-hfa-v3.c @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios -target-feature +neon -target-abi darwinpcs -fallow-half-arguments-and-returns -emit-llvm -o - %s | FileCheck %s + +typedef __attribute__((__ext_vector_type__(16))) signed char int8x16_t; +typedef __attribute__((__ext_vector_type__(3))) float float32x3_t; + +// CHECK: %struct.HFAv3 = type { [4 x <3 x float>] } +typedef struct { float32x3_t arr[4]; } HFAv3; + +// CHECK: %struct.MixedHFAv3 = type { [3 x <3 x float>], <16 x i8> } +typedef struct { float32x3_t arr[3]; int8x16_t b; } MixedHFAv3; + +// CHECK: define %struct.HFAv3 @test([4 x <4 x float>] %{{.*}}, [4 x <4 x float>] %{{.*}}, [4 x <4 x float>] %{{.*}}) +HFAv3 test(HFAv3 a0, HFAv3 a1, HFAv3 a2) { + return a2; +} + +// CHECK: define %struct.MixedHFAv3 @test_mixed([4 x <4 x float>] %{{.*}}, [4 x <4 x float>] %{{.*}}, [4 x <4 x float>] %{{.*}}) +MixedHFAv3 test_mixed(MixedHFAv3 a0, MixedHFAv3 a1, MixedHFAv3 a2) { + return a2; +} diff --git a/test/CodeGen/arm64-arguments.c b/test/CodeGen/arm64-arguments.c index 93a1a198955..f90b8e3b93a 100644 --- a/test/CodeGen/arm64-arguments.c +++ b/test/CodeGen/arm64-arguments.c @@ -714,3 +714,34 @@ int32x4_t test_toobig_hva(int n, ...) { struct TooBigHVA h = __builtin_va_arg(thelist, struct TooBigHVA); return h.d; } + +typedef __attribute__((__ext_vector_type__(3))) float float32x3_t; +typedef struct { float32x3_t arr[4]; } HFAv3; + +float32x3_t test_hva_v3(int n, ...) { +// CHECK-LABEL: define <3 x float> @test_hva_v3(i32 %n, ...) +// CHECK: [[THELIST:%.*]] = alloca i8* +// CHECK: [[CURLIST:%.*]] = load i8*, i8** [[THELIST]] + + // HVA is not indirect, so occupies its full 16 bytes on the stack. but it + // must be properly aligned. +// CHECK: [[ALIGN0:%.*]] = ptrtoint i8* [[CURLIST]] to i64 +// CHECK: [[ALIGN1:%.*]] = add i64 [[ALIGN0]], 15 +// CHECK: [[ALIGN2:%.*]] = and i64 [[ALIGN1]], -16 +// CHECK: [[ALIGNED_LIST:%.*]] = inttoptr i64 [[ALIGN2]] to i8* + +// CHECK: [[NEXTLIST:%.*]] = getelementptr inbounds i8, i8* [[ALIGNED_LIST]], i64 64 +// CHECK: store i8* [[NEXTLIST]], i8** [[THELIST]] + +// CHECK: bitcast i8* [[ALIGNED_LIST]] to %struct.HFAv3* + __builtin_va_list l; + __builtin_va_start(l, n); + HFAv3 r = __builtin_va_arg(l, HFAv3); + return r.arr[2]; +} + +float32x3_t test_hva_v3_call(HFAv3 *a) { +// CHECK-LABEL: define <3 x float> @test_hva_v3_call(%struct.HFAv3* %a) +// CHECK: call <3 x float> (i32, ...) @test_hva_v3(i32 1, [4 x <4 x float>] {{.*}}) + return test_hva_v3(1, *a); +} diff --git a/test/CodeGen/ppc64le-aggregates.c b/test/CodeGen/ppc64le-aggregates.c index 3ad4b06c688..04d2fb4766e 100644 --- a/test/CodeGen/ppc64le-aggregates.c +++ b/test/CodeGen/ppc64le-aggregates.c @@ -255,84 +255,84 @@ struct v3f9 { float3 v[9]; }; struct v3fab { float3 a; float3 b; }; struct v3fabc { float3 a; float3 b; float3 c; }; -// CHECK: define [1 x <3 x float>] @func_v3f1(<3 x float> inreg %x.coerce) +// CHECK: define [1 x <4 x float>] @func_v3f1(<3 x float> inreg %x.coerce) struct v3f1 func_v3f1(struct v3f1 x) { return x; } -// CHECK: define [2 x <3 x float>] @func_v3f2([2 x <3 x float>] %x.coerce) +// CHECK: define [2 x <4 x float>] @func_v3f2([2 x <4 x float>] %x.coerce) struct v3f2 func_v3f2(struct v3f2 x) { return x; } -// CHECK: define [3 x <3 x float>] @func_v3f3([3 x <3 x float>] %x.coerce) +// CHECK: define [3 x <4 x float>] @func_v3f3([3 x <4 x float>] %x.coerce) struct v3f3 func_v3f3(struct v3f3 x) { return x; } -// CHECK: define [4 x <3 x float>] @func_v3f4([4 x <3 x float>] %x.coerce) +// CHECK: define [4 x <4 x float>] @func_v3f4([4 x <4 x float>] %x.coerce) struct v3f4 func_v3f4(struct v3f4 x) { return x; } -// CHECK: define [5 x <3 x float>] @func_v3f5([5 x <3 x float>] %x.coerce) +// CHECK: define [5 x <4 x float>] @func_v3f5([5 x <4 x float>] %x.coerce) struct v3f5 func_v3f5(struct v3f5 x) { return x; } -// CHECK: define [6 x <3 x float>] @func_v3f6([6 x <3 x float>] %x.coerce) +// CHECK: define [6 x <4 x float>] @func_v3f6([6 x <4 x float>] %x.coerce) struct v3f6 func_v3f6(struct v3f6 x) { return x; } -// CHECK: define [7 x <3 x float>] @func_v3f7([7 x <3 x float>] %x.coerce) +// CHECK: define [7 x <4 x float>] @func_v3f7([7 x <4 x float>] %x.coerce) struct v3f7 func_v3f7(struct v3f7 x) { return x; } -// CHECK: define [8 x <3 x float>] @func_v3f8([8 x <3 x float>] %x.coerce) +// CHECK: define [8 x <4 x float>] @func_v3f8([8 x <4 x float>] %x.coerce) struct v3f8 func_v3f8(struct v3f8 x) { return x; } // CHECK: define void @func_v3f9(%struct.v3f9* noalias sret %agg.result, %struct.v3f9* byval align 16 %x) struct v3f9 func_v3f9(struct v3f9 x) { return x; } -// CHECK: define [2 x <3 x float>] @func_v3fab([2 x <3 x float>] %x.coerce) +// CHECK: define [2 x <4 x float>] @func_v3fab([2 x <4 x float>] %x.coerce) struct v3fab func_v3fab(struct v3fab x) { return x; } -// CHECK: define [3 x <3 x float>] @func_v3fabc([3 x <3 x float>] %x.coerce) +// CHECK: define [3 x <4 x float>] @func_v3fabc([3 x <4 x float>] %x.coerce) struct v3fabc func_v3fabc(struct v3fabc x) { return x; } // CHECK-LABEL: @call_v3f1 // CHECK: %[[TMP:[^ ]+]] = load <3 x float>, <3 x float>* getelementptr inbounds (%struct.v3f1, %struct.v3f1* @global_v3f1, i32 0, i32 0, i32 0), align 1 -// CHECK: call [1 x <3 x float>] @func_v3f1(<3 x float> inreg %[[TMP]]) +// CHECK: call [1 x <4 x float>] @func_v3f1(<3 x float> inreg %[[TMP]]) struct v3f1 global_v3f1; void call_v3f1(void) { global_v3f1 = func_v3f1(global_v3f1); } // CHECK-LABEL: @call_v3f2 -// CHECK: %[[TMP:[^ ]+]] = load [2 x <3 x float>], [2 x <3 x float>]* getelementptr inbounds (%struct.v3f2, %struct.v3f2* @global_v3f2, i32 0, i32 0), align 1 -// CHECK: call [2 x <3 x float>] @func_v3f2([2 x <3 x float>] %[[TMP]]) +// CHECK: %[[TMP:[^ ]+]] = load [2 x <4 x float>], [2 x <4 x float>]* bitcast (%struct.v3f2* @global_v3f2 to [2 x <4 x float>]*), align 16 +// CHECK: call [2 x <4 x float>] @func_v3f2([2 x <4 x float>] %[[TMP]]) struct v3f2 global_v3f2; void call_v3f2(void) { global_v3f2 = func_v3f2(global_v3f2); } // CHECK-LABEL: @call_v3f3 -// CHECK: %[[TMP:[^ ]+]] = load [3 x <3 x float>], [3 x <3 x float>]* getelementptr inbounds (%struct.v3f3, %struct.v3f3* @global_v3f3, i32 0, i32 0), align 1 -// CHECK: call [3 x <3 x float>] @func_v3f3([3 x <3 x float>] %[[TMP]]) +// CHECK: %[[TMP:[^ ]+]] = load [3 x <4 x float>], [3 x <4 x float>]* bitcast (%struct.v3f3* @global_v3f3 to [3 x <4 x float>]*), align 16 +// CHECK: call [3 x <4 x float>] @func_v3f3([3 x <4 x float>] %[[TMP]]) struct v3f3 global_v3f3; void call_v3f3(void) { global_v3f3 = func_v3f3(global_v3f3); } // CHECK-LABEL: @call_v3f4 -// CHECK: %[[TMP:[^ ]+]] = load [4 x <3 x float>], [4 x <3 x float>]* getelementptr inbounds (%struct.v3f4, %struct.v3f4* @global_v3f4, i32 0, i32 0), align 1 -// CHECK: call [4 x <3 x float>] @func_v3f4([4 x <3 x float>] %[[TMP]]) +// CHECK: %[[TMP:[^ ]+]] = load [4 x <4 x float>], [4 x <4 x float>]* bitcast (%struct.v3f4* @global_v3f4 to [4 x <4 x float>]*), align 16 +// CHECK: call [4 x <4 x float>] @func_v3f4([4 x <4 x float>] %[[TMP]]) struct v3f4 global_v3f4; void call_v3f4(void) { global_v3f4 = func_v3f4(global_v3f4); } // CHECK-LABEL: @call_v3f5 -// CHECK: %[[TMP:[^ ]+]] = load [5 x <3 x float>], [5 x <3 x float>]* getelementptr inbounds (%struct.v3f5, %struct.v3f5* @global_v3f5, i32 0, i32 0), align 1 -// CHECK: call [5 x <3 x float>] @func_v3f5([5 x <3 x float>] %[[TMP]]) +// CHECK: %[[TMP:[^ ]+]] = load [5 x <4 x float>], [5 x <4 x float>]* bitcast (%struct.v3f5* @global_v3f5 to [5 x <4 x float>]*), align 16 +// CHECK: call [5 x <4 x float>] @func_v3f5([5 x <4 x float>] %[[TMP]]) struct v3f5 global_v3f5; void call_v3f5(void) { global_v3f5 = func_v3f5(global_v3f5); } // CHECK-LABEL: @call_v3f6 -// CHECK: %[[TMP:[^ ]+]] = load [6 x <3 x float>], [6 x <3 x float>]* getelementptr inbounds (%struct.v3f6, %struct.v3f6* @global_v3f6, i32 0, i32 0), align 1 -// CHECK: call [6 x <3 x float>] @func_v3f6([6 x <3 x float>] %[[TMP]]) +// CHECK: %[[TMP:[^ ]+]] = load [6 x <4 x float>], [6 x <4 x float>]* bitcast (%struct.v3f6* @global_v3f6 to [6 x <4 x float>]*), align 16 +// CHECK: call [6 x <4 x float>] @func_v3f6([6 x <4 x float>] %[[TMP]]) struct v3f6 global_v3f6; void call_v3f6(void) { global_v3f6 = func_v3f6(global_v3f6); } // CHECK-LABEL: @call_v3f7 -// CHECK: %[[TMP:[^ ]+]] = load [7 x <3 x float>], [7 x <3 x float>]* getelementptr inbounds (%struct.v3f7, %struct.v3f7* @global_v3f7, i32 0, i32 0), align 1 -// CHECK: call [7 x <3 x float>] @func_v3f7([7 x <3 x float>] %[[TMP]]) +// CHECK: %[[TMP:[^ ]+]] = load [7 x <4 x float>], [7 x <4 x float>]* bitcast (%struct.v3f7* @global_v3f7 to [7 x <4 x float>]*), align 16 +// CHECK: call [7 x <4 x float>] @func_v3f7([7 x <4 x float>] %[[TMP]]) struct v3f7 global_v3f7; void call_v3f7(void) { global_v3f7 = func_v3f7(global_v3f7); } // CHECK-LABEL: @call_v3f8 -// CHECK: %[[TMP:[^ ]+]] = load [8 x <3 x float>], [8 x <3 x float>]* getelementptr inbounds (%struct.v3f8, %struct.v3f8* @global_v3f8, i32 0, i32 0), align 1 -// CHECK: call [8 x <3 x float>] @func_v3f8([8 x <3 x float>] %[[TMP]]) +// CHECK: %[[TMP:[^ ]+]] = load [8 x <4 x float>], [8 x <4 x float>]* bitcast (%struct.v3f8* @global_v3f8 to [8 x <4 x float>]*), align 16 +// CHECK: call [8 x <4 x float>] @func_v3f8([8 x <4 x float>] %[[TMP]]) struct v3f8 global_v3f8; void call_v3f8(void) { global_v3f8 = func_v3f8(global_v3f8); } @@ -342,14 +342,14 @@ struct v3f9 global_v3f9; void call_v3f9(void) { global_v3f9 = func_v3f9(global_v3f9); } // CHECK-LABEL: @call_v3fab -// CHECK: %[[TMP:[^ ]+]] = load [2 x <3 x float>], [2 x <3 x float>]* bitcast (%struct.v3fab* @global_v3fab to [2 x <3 x float>]*) -// CHECK: call [2 x <3 x float>] @func_v3fab([2 x <3 x float>] %[[TMP]]) +// CHECK: %[[TMP:[^ ]+]] = load [2 x <4 x float>], [2 x <4 x float>]* bitcast (%struct.v3fab* @global_v3fab to [2 x <4 x float>]*), align 16 +// CHECK: call [2 x <4 x float>] @func_v3fab([2 x <4 x float>] %[[TMP]]) struct v3fab global_v3fab; void call_v3fab(void) { global_v3fab = func_v3fab(global_v3fab); } // CHECK-LABEL: @call_v3fabc -// CHECK: %[[TMP:[^ ]+]] = load [3 x <3 x float>], [3 x <3 x float>]* bitcast (%struct.v3fabc* @global_v3fabc to [3 x <3 x float>]*) -// CHECK: call [3 x <3 x float>] @func_v3fabc([3 x <3 x float>] %[[TMP]]) +// CHECK: %[[TMP:[^ ]+]] = load [3 x <4 x float>], [3 x <4 x float>]* bitcast (%struct.v3fabc* @global_v3fabc to [3 x <4 x float>]*), align 16 +// CHECK: call [3 x <4 x float>] @func_v3fabc([3 x <4 x float>] %[[TMP]]) struct v3fabc global_v3fabc; void call_v3fabc(void) { global_v3fabc = func_v3fabc(global_v3fabc); } From 8bc34e1cd7dd7624db1009c2d0d8a5566dec15a8 Mon Sep 17 00:00:00 2001 From: Ahmed Bougacha Date: Tue, 19 Apr 2016 19:44:45 +0000 Subject: [PATCH 519/742] [ARM NEON] Define vfms_f32 on ARM, and all vfms using vfma. r259537 added vfma/vfms to armv7, but the builtin was only lowered on the AArch64 side. Instead of supporting it on ARM, get rid of it. The vfms builtin lowered to: %nb = fsub float -0.0, %b %r = @llvm.fma.f32(%a, %nb, %c) Instead, define the operation in terms of vfma, and swap the multiplicands. It now lowers to: %na = fsub float -0.0, %a %r = @llvm.fma.f32(%na, %b, %c) This matches the instruction more closely, and lets current LLVM generate the "natural" operand ordering: fmls.2s v0, v1, v2 instead of the crooked (but equivalent): fmls.2s v0, v2, v1 Except for theses changes, assembly is identical. LLVM accepts both commutations, and the LLVM tests in: test/CodeGen/AArch64/arm64-fmadd.ll test/CodeGen/AArch64/fp-dp3.ll test/CodeGen/AArch64/neon-fma.ll test/CodeGen/ARM/fusedMAC.ll already check either the new one only, or both. Also verified against the test-suite unittests. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@266807 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit fa8ab2562a582a60fb7dff9d873b65d84ab864f4) Conflicts: test/CodeGen/aarch64-neon-2velem.c test/CodeGen/aarch64-neon-fma.c test/CodeGen/aarch64-neon-intrinsics.c test/CodeGen/aarch64-neon-scalar-x-indexed-elem.c --- include/clang/Basic/arm_neon.td | 11 ++++---- lib/CodeGen/CGBuiltin.cpp | 16 ----------- .../aarch64-neon-scalar-x-indexed-elem.c | 3 -- test/CodeGen/arm64_vfma.c | 24 ++++++++-------- test/CodeGen/arm_neon_intrinsics.c | 28 +++++++++++++++++++ 5 files changed, 46 insertions(+), 36 deletions(-) diff --git a/include/clang/Basic/arm_neon.td b/include/clang/Basic/arm_neon.td index 4863566653b..6641ed2edee 100644 --- a/include/clang/Basic/arm_neon.td +++ b/include/clang/Basic/arm_neon.td @@ -339,6 +339,7 @@ def OP_MLALHi : Op<(call "vmlal", $p0, (call "vget_high", $p1), (call "vget_high", $p2))>; def OP_MLALHi_N : Op<(call "vmlal_n", $p0, (call "vget_high", $p1), $p2)>; def OP_MLS : Op<(op "-", $p0, (op "*", $p1, $p2))>; +def OP_FMLS : Op<(call "vfma", $p0, (op "-", $p1), $p2)>; def OP_MLSL : Op<(op "-", $p0, (call "vmull", $p1, $p2))>; def OP_MLSLHi : Op<(call "vmlsl", $p0, (call "vget_high", $p1), (call "vget_high", $p2))>; @@ -347,7 +348,7 @@ def OP_MUL_N : Op<(op "*", $p0, (dup $p1))>; def OP_MLA_N : Op<(op "+", $p0, (op "*", $p1, (dup $p2)))>; def OP_MLS_N : Op<(op "-", $p0, (op "*", $p1, (dup $p2)))>; def OP_FMLA_N : Op<(call "vfma", $p0, $p1, (dup $p2))>; -def OP_FMLS_N : Op<(call "vfms", $p0, $p1, (dup $p2))>; +def OP_FMLS_N : Op<(call "vfma", $p0, (op "-", $p1), (dup $p2))>; def OP_MLAL_N : Op<(op "+", $p0, (call "vmull", $p1, (dup $p2)))>; def OP_MLSL_N : Op<(op "-", $p0, (call "vmull", $p1, (dup $p2)))>; def OP_MUL_LN : Op<(op "*", $p0, (splat $p1, $p2))>; @@ -377,8 +378,8 @@ def OP_QRDMLAH : Op<(call "vqadd", $p0, (call "vqrdmulh", $p1, $p2))>; def OP_QRDMLSH : Op<(call "vqsub", $p0, (call "vqrdmulh", $p1, $p2))>; def OP_QRDMLAH_LN : Op<(call "vqadd", $p0, (call "vqrdmulh", $p1, (splat $p2, $p3)))>; def OP_QRDMLSH_LN : Op<(call "vqsub", $p0, (call "vqrdmulh", $p1, (splat $p2, $p3)))>; -def OP_FMS_LN : Op<(call "vfma_lane", $p0, $p1, (op "-", $p2), $p3)>; -def OP_FMS_LNQ : Op<(call "vfma_laneq", $p0, $p1, (op "-", $p2), $p3)>; +def OP_FMS_LN : Op<(call "vfma_lane", $p0, (op "-", $p1), $p2, $p3)>; +def OP_FMS_LNQ : Op<(call "vfma_laneq", $p0, (op "-", $p1), $p2, $p3)>; def OP_TRN1 : Op<(shuffle $p0, $p1, (interleave (decimate mask0, 2), (decimate mask1, 2)))>; def OP_ZIP1 : Op<(shuffle $p0, $p1, (lowhalf (interleave mask0, mask1)))>; @@ -826,7 +827,7 @@ def VREINTERPRET let ArchGuard = "defined(__ARM_FEATURE_FMA)" in { def VFMA : SInst<"vfma", "dddd", "fQf">; - def VFMS : SInst<"vfms", "dddd", "fQf">; + def VFMS : SOpInst<"vfms", "dddd", "fQf", OP_FMLS>; } //////////////////////////////////////////////////////////////////////////////// @@ -911,7 +912,7 @@ def FDIV : IOpInst<"vdiv", "ddd", "fdQfQd", OP_DIV>; //////////////////////////////////////////////////////////////////////////////// // Vector fused multiply-add operations def FMLA : SInst<"vfma", "dddd", "dQd">; -def FMLS : SInst<"vfms", "dddd", "dQd">; +def FMLS : SOpInst<"vfms", "dddd", "dQd", OP_FMLS>; //////////////////////////////////////////////////////////////////////////////// // MUL, MLA, MLS, FMA, FMS definitions with scalar argument diff --git a/lib/CodeGen/CGBuiltin.cpp b/lib/CodeGen/CGBuiltin.cpp index c73aa222b78..55f919e6c89 100644 --- a/lib/CodeGen/CGBuiltin.cpp +++ b/lib/CodeGen/CGBuiltin.cpp @@ -5148,22 +5148,6 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID, Ops[2] = Builder.CreateExtractElement(Ops[2], Ops[3], "extract"); return Builder.CreateCall(F, {Ops[1], Ops[2], Ops[0]}); } - case NEON::BI__builtin_neon_vfms_v: - case NEON::BI__builtin_neon_vfmsq_v: { // Only used for FP types - // FIXME: probably remove when we no longer support aarch64_simd.h - // (arm_neon.h delegates to vfma). - - // The ARM builtins (and instructions) have the addend as the first - // operand, but the 'fma' intrinsics have it last. Swap it around here. - Value *Subtrahend = Ops[0]; - Value *Multiplicand = Ops[2]; - Ops[0] = Multiplicand; - Ops[2] = Subtrahend; - Ops[1] = Builder.CreateBitCast(Ops[1], VTy); - Ops[1] = Builder.CreateFNeg(Ops[1]); - Int = Intrinsic::fma; - return EmitNeonCall(CGM.getIntrinsic(Int, Ty), Ops, "fmls"); - } case NEON::BI__builtin_neon_vmull_v: // FIXME: improve sharing scheme to cope with 3 alternative LLVM intrinsics. Int = usgn ? Intrinsic::aarch64_neon_umull : Intrinsic::aarch64_neon_smull; diff --git a/test/CodeGen/aarch64-neon-scalar-x-indexed-elem.c b/test/CodeGen/aarch64-neon-scalar-x-indexed-elem.c index 4c2f4d72d89..a9d46cd46ee 100644 --- a/test/CodeGen/aarch64-neon-scalar-x-indexed-elem.c +++ b/test/CodeGen/aarch64-neon-scalar-x-indexed-elem.c @@ -99,7 +99,6 @@ float64_t test_vfmad_laneq_f64(float64_t a, float64_t b, float64x2_t c) { // CHECK: fmla {{d[0-9]+}}, {{d[0-9]+}}, {{v[0-9]+}}.d[1] } -// CHECK-LABEL: test_vfmss_lane_f32 float32_t test_vfmss_lane_f32(float32_t a, float32_t b, float32x2_t c) { return vfmss_lane_f32(a, b, c, 1); // CHECK: fmls {{s[0-9]+}}, {{s[0-9]+}}, {{v[0-9]+}}.s[1] @@ -111,7 +110,6 @@ float64x1_t test_vfma_lane_f64(float64x1_t a, float64x1_t b, float64x1_t v) { // CHECK: {{fmla|fmadd}} {{d[0-9]+}}, {{d[0-9]+}}, {{v[0-9]+.d\[0\]|d[0-9]+}} } -// CHECK-LABEL: test_vfms_lane_f64 float64x1_t test_vfms_lane_f64(float64x1_t a, float64x1_t b, float64x1_t v) { return vfms_lane_f64(a, b, v, 0); // CHECK: {{fmls|fmsub}} {{d[0-9]+}}, {{d[0-9]+}}, {{v[0-9]+.d\[0\]|d[0-9]+}} @@ -123,7 +121,6 @@ float64x1_t test_vfma_laneq_f64(float64x1_t a, float64x1_t b, float64x2_t v) { // CHECK: fmla {{d[0-9]+}}, {{d[0-9]+}}, {{v[0-9]+}}.d[0] } -// CHECK-LABEL: test_vfms_laneq_f64 float64x1_t test_vfms_laneq_f64(float64x1_t a, float64x1_t b, float64x2_t v) { return vfms_laneq_f64(a, b, v, 0); // CHECK: fmls {{d[0-9]+}}, {{d[0-9]+}}, {{v[0-9]+}}.d[0] diff --git a/test/CodeGen/arm64_vfma.c b/test/CodeGen/arm64_vfma.c index bfa56877963..12f31113adc 100644 --- a/test/CodeGen/arm64_vfma.c +++ b/test/CodeGen/arm64_vfma.c @@ -82,7 +82,7 @@ float32x2_t test_vfms_f32(float32x2_t a1, float32x2_t a2, float32x2_t a3) { // CHECK: test_vfms_f32 return vfms_f32(a1, a2, a3); // CHECK: [[NEG:%.*]] = fsub <2 x float> {{.*}}, %a2 - // CHECK: llvm.fma.v2f32(<2 x float> %a3, <2 x float> [[NEG]], <2 x float> %a1) + // CHECK: llvm.fma.v2f32(<2 x float> [[NEG]], <2 x float> %a3, <2 x float> %a1) // CHECK-NEXT: ret } @@ -90,7 +90,7 @@ float32x4_t test_vfmsq_f32(float32x4_t a1, float32x4_t a2, float32x4_t a3) { // CHECK: test_vfmsq_f32 return vfmsq_f32(a1, a2, a3); // CHECK: [[NEG:%.*]] = fsub <4 x float> {{.*}}, %a2 - // CHECK: llvm.fma.v4f32(<4 x float> %a3, <4 x float> [[NEG]], <4 x float> %a1) + // CHECK: llvm.fma.v4f32(<4 x float> [[NEG]], <4 x float> %a3, <4 x float> %a1) // CHECK-NEXT: ret } @@ -98,7 +98,7 @@ float64x2_t test_vfmsq_f64(float64x2_t a1, float64x2_t a2, float64x2_t a3) { // CHECK: test_vfmsq_f64 return vfmsq_f64(a1, a2, a3); // CHECK: [[NEG:%.*]] = fsub <2 x double> {{.*}}, %a2 - // CHECK: llvm.fma.v2f64(<2 x double> %a3, <2 x double> [[NEG]], <2 x double> %a1) + // CHECK: llvm.fma.v2f64(<2 x double> [[NEG]], <2 x double> %a3, <2 x double> %a1) // CHECK-NEXT: ret } @@ -107,9 +107,9 @@ float32x2_t test_vfms_lane_f32(float32x2_t a1, float32x2_t a2, float32x2_t a3) { return vfms_lane_f32(a1, a2, a3, 1); // NB: the test below is deliberately lose, so that we don't depend too much // upon the exact IR used to select lane 1 (usually a shufflevector) - // CHECK: [[NEG:%.*]] = fsub <2 x float> {{.*}}, %a3 - // CHECK: [[LANE:%.*]] = shufflevector <2 x float> [[NEG]] - // CHECK: llvm.fma.v2f32(<2 x float> {{.*}}, <2 x float> [[LANE]], <2 x float> %a1) + // CHECK: [[NEG:%.*]] = fsub <2 x float> {{.*}}, %a2 + // CHECK: [[LANE:%.*]] = shufflevector <2 x float> %a3 + // CHECK: llvm.fma.v2f32(<2 x float> [[NEG]], <2 x float> [[LANE]], <2 x float> %a1) // CHECK-NEXT: ret } @@ -118,9 +118,9 @@ float32x4_t test_vfmsq_lane_f32(float32x4_t a1, float32x4_t a2, float32x2_t a3) return vfmsq_lane_f32(a1, a2, a3, 1); // NB: the test below is deliberately lose, so that we don't depend too much // upon the exact IR used to select lane 1 (usually a shufflevector) - // CHECK: [[NEG:%.*]] = fsub <2 x float> {{.*}}, %a3 - // CHECK: [[LANE:%.*]] = shufflevector <2 x float> [[NEG]] - // CHECK: llvm.fma.v4f32(<4 x float> {{.*}}, <4 x float> [[LANE]], <4 x float> %a1) + // CHECK: [[NEG:%.*]] = fsub <4 x float> {{.*}}, %a2 + // CHECK: [[LANE:%.*]] = shufflevector <2 x float> %a3 + // CHECK: llvm.fma.v4f32(<4 x float> [[NEG]], <4 x float> [[LANE]], <4 x float> %a1) // CHECK-NEXT: ret } @@ -129,8 +129,8 @@ float64x2_t test_vfmsq_lane_f64(float64x2_t a1, float64x2_t a2, float64x1_t a3) return vfmsq_lane_f64(a1, a2, a3, 0); // NB: the test below is deliberately lose, so that we don't depend too much // upon the exact IR used to select lane 1 (usually a shufflevector) - // CHECK: [[NEG:%.*]] = fsub <1 x double> {{.*}}, %a3 - // CHECK: [[LANE:%.*]] = shufflevector <1 x double> [[NEG]] - // CHECK: llvm.fma.v2f64(<2 x double> {{.*}}, <2 x double> [[LANE]], <2 x double> %a1) + // CHECK: [[NEG:%.*]] = fsub <2 x double> {{.*}}, %a2 + // CHECK: [[LANE:%.*]] = shufflevector <1 x double> %a3 + // CHECK: llvm.fma.v2f64(<2 x double> [[NEG]], <2 x double> [[LANE]], <2 x double> %a1) // CHECK-NEXT: ret } diff --git a/test/CodeGen/arm_neon_intrinsics.c b/test/CodeGen/arm_neon_intrinsics.c index d92c32c476a..3a87211abd7 100644 --- a/test/CodeGen/arm_neon_intrinsics.c +++ b/test/CodeGen/arm_neon_intrinsics.c @@ -2283,6 +2283,34 @@ float32x4_t test_vfmaq_f32(float32x4_t a, float32x4_t b, float32x4_t c) { return vfmaq_f32(a, b, c); } +// CHECK-LABEL: define <2 x float> @test_vfms_f32(<2 x float> %a, <2 x float> %b, <2 x float> %c) #0 { +// CHECK: [[SUB_I:%.*]] = fsub <2 x float> , %b +// CHECK: [[TMP0:%.*]] = bitcast <2 x float> %a to <8 x i8> +// CHECK: [[TMP1:%.*]] = bitcast <2 x float> [[SUB_I]] to <8 x i8> +// CHECK: [[TMP2:%.*]] = bitcast <2 x float> %c to <8 x i8> +// CHECK: [[TMP3:%.*]] = bitcast <8 x i8> [[TMP0]] to <2 x float> +// CHECK: [[TMP4:%.*]] = bitcast <8 x i8> [[TMP1]] to <2 x float> +// CHECK: [[TMP5:%.*]] = bitcast <8 x i8> [[TMP2]] to <2 x float> +// CHECK: [[TMP6:%.*]] = call <2 x float> @llvm.fma.v2f32(<2 x float> [[TMP4]], <2 x float> [[TMP5]], <2 x float> [[TMP3]]) #4 +// CHECK: ret <2 x float> [[TMP6]] +float32x2_t test_vfms_f32(float32x2_t a, float32x2_t b, float32x2_t c) { + return vfms_f32(a, b, c); +} + +// CHECK-LABEL: define <4 x float> @test_vfmsq_f32(<4 x float> %a, <4 x float> %b, <4 x float> %c) #0 { +// CHECK: [[SUB_I:%.*]] = fsub <4 x float> , %b +// CHECK: [[TMP0:%.*]] = bitcast <4 x float> %a to <16 x i8> +// CHECK: [[TMP1:%.*]] = bitcast <4 x float> [[SUB_I]] to <16 x i8> +// CHECK: [[TMP2:%.*]] = bitcast <4 x float> %c to <16 x i8> +// CHECK: [[TMP3:%.*]] = bitcast <16 x i8> [[TMP0]] to <4 x float> +// CHECK: [[TMP4:%.*]] = bitcast <16 x i8> [[TMP1]] to <4 x float> +// CHECK: [[TMP5:%.*]] = bitcast <16 x i8> [[TMP2]] to <4 x float> +// CHECK: [[TMP6:%.*]] = call <4 x float> @llvm.fma.v4f32(<4 x float> [[TMP4]], <4 x float> [[TMP5]], <4 x float> [[TMP3]]) #4 +// CHECK: ret <4 x float> [[TMP6]] +float32x4_t test_vfmsq_f32(float32x4_t a, float32x4_t b, float32x4_t c) { + return vfmsq_f32(a, b, c); +} + // CHECK-LABEL: test_vget_high_s8 int8x8_t test_vget_high_s8(int8x16_t a) { From 00fa8216f00625eebde15c06b17b9bf15bad498b Mon Sep 17 00:00:00 2001 From: Bob Wilson Date: Thu, 21 Apr 2016 00:11:24 +0000 Subject: [PATCH 520/742] Remove the (ignored) -Wreceived-is-weak diagnostic. We kept this around for a while since Xcode 6 and earlier had a build setting for this warning. It was removed in Xcode 7 so there should be no need for this warning now. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@266938 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticGroups.td | 2 -- test/SemaObjC/iboutlet.m | 5 ++--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td index 8e5f57d6d89..c97ae75329c 100644 --- a/include/clang/Basic/DiagnosticGroups.td +++ b/include/clang/Basic/DiagnosticGroups.td @@ -283,8 +283,6 @@ def : DiagGroup<"overflow">; def ForwardClassReceiver : DiagGroup<"receiver-forward-class">; def MethodAccess : DiagGroup<"objc-method-access">; def ObjCReceiver : DiagGroup<"receiver-expr">; -// FIXME: Remove this when Xcode removes the warning setting. -def : DiagGroup<"receiver-is-weak">; def OperatorNewReturnsNull : DiagGroup<"new-returns-null">; def OverlengthStrings : DiagGroup<"overlength-strings">; def OverloadedVirtual : DiagGroup<"overloaded-virtual">; diff --git a/test/SemaObjC/iboutlet.m b/test/SemaObjC/iboutlet.m index 597c4e46287..2ce4ce1d87a 100644 --- a/test/SemaObjC/iboutlet.m +++ b/test/SemaObjC/iboutlet.m @@ -1,7 +1,6 @@ -// RUN: %clang_cc1 -fsyntax-only -fobjc-arc -Wno-objc-root-class -Wreceiver-is-weak -Warc-repeated-use-of-weak -fobjc-runtime-has-weak -verify %s -// RUN: %clang_cc1 -x objective-c++ -fsyntax-only -fobjc-arc -Wno-objc-root-class -Wreceiver-is-weak -Warc-repeated-use-of-weak -fobjc-runtime-has-weak -verify %s +// RUN: %clang_cc1 -fsyntax-only -fobjc-arc -Wno-objc-root-class -Warc-repeated-use-of-weak -fobjc-runtime-has-weak -verify %s +// RUN: %clang_cc1 -x objective-c++ -fsyntax-only -fobjc-arc -Wno-objc-root-class -Warc-repeated-use-of-weak -fobjc-runtime-has-weak -verify %s // rdar://11448209 -// rdar://20259376 #define READONLY readonly From 4611d2800553b7749cf4d05c3dba5c27074cb52c Mon Sep 17 00:00:00 2001 From: Manman Ren Date: Tue, 19 Apr 2016 19:05:03 +0000 Subject: [PATCH 521/742] ObjC Class Property: don't emit class properties on old deployment targets. For old deployment targets, emit nil for all class property lists. rdar://25616128 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@266800 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CGObjCMac.cpp | 9 +++++++++ test/CodeGenObjC/metadata-class-properties.m | 15 +++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index 9c681988510..29eecf5408c 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/lib/CodeGen/CGObjCMac.cpp @@ -2956,6 +2956,15 @@ llvm::Constant *CGObjCCommonMac::EmitPropertyList(Twine Name, const ObjCContainerDecl *OCD, const ObjCCommonTypesHelper &ObjCTypes, bool IsClassProperty) { + if (IsClassProperty) { + // Make this entry NULL for OS X with deployment target < 10.11, for iOS + // with deployment target < 9.0. + const llvm::Triple &Triple = CGM.getTarget().getTriple(); + if ((Triple.isMacOSX() && Triple.isMacOSXVersionLT(10, 11)) || + (Triple.isiOS() && Triple.isOSVersionLT(9))) + return llvm::Constant::getNullValue(ObjCTypes.PropertyListPtrTy); + } + SmallVector Properties; llvm::SmallPtrSet PropertySet; diff --git a/test/CodeGenObjC/metadata-class-properties.m b/test/CodeGenObjC/metadata-class-properties.m index 2def20025c9..58841bc1230 100644 --- a/test/CodeGenObjC/metadata-class-properties.m +++ b/test/CodeGenObjC/metadata-class-properties.m @@ -1,5 +1,6 @@ -// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -o - %s | FileCheck %s -// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -o - -fobjc-runtime=macosx-fragile-10.5 %s | FileCheck -check-prefix=CHECK-FRAGILE %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.11 -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.10 -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-NULL %s +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.11 -emit-llvm -o - -fobjc-runtime=macosx-fragile-10.5 %s | FileCheck -check-prefix=CHECK-FRAGILE %s // CHECK: @"\01l_OBJC_$_CLASS_PROP_LIST_Proto" = private global {{.*}} section "__DATA, __objc_const", align 8 // CHECK: @"\01l_OBJC_PROTOCOL_$_Proto" = {{.*}} global %struct._protocol_t { {{.*}} i32 96, i32 {{.*}} @"\01l_OBJC_$_CLASS_PROP_LIST_Proto" {{.*}} } @@ -11,6 +12,16 @@ // CHECK: !{i32 1, !"Objective-C Class Properties", i32 64} +// CHECK-NULL-NOT: @"\01l_OBJC_$_CLASS_PROP_LIST_Proto" +// CHECK-NULL: @"\01l_OBJC_PROTOCOL_$_Proto" = {{.*}} global %struct._protocol_t { {{.*}} %struct._prop_list_t* null, i32 96, i32 {{.*}} %struct._prop_list_t* null } +// CHECK-NULL-NOT: @"\01l_OBJC_$_CLASS_PROP_LIST_Foo_$_Category" = private global {{.*}} section "__DATA, __objc_const", align 8 +// CHECK-NULL: @"\01l_OBJC_$_CATEGORY_Foo_$_Category" = private global %struct._category_t { {{.*}} %struct._prop_list_t* null, %struct._prop_list_t* null, {{.*}} }, section "__DATA, __objc_const", align 8 + +// CHECK-NULL-NOT: @"\01l_OBJC_$_CLASS_PROP_LIST_C" = private global {{.*}} section "__DATA, __objc_const", align 8 +// CHECK-NULL: @"\01l_OBJC_METACLASS_RO_$_C" = private global %struct._class_ro_t { {{.*}} %struct._prop_list_t* null }, section "__DATA, __objc_const", align 8 + +// CHECK-NULL: !{i32 1, !"Objective-C Class Properties", i32 64} + // CHECK-FRAGILE: @"OBJC_$_CLASS_PROP_PROTO_LIST_Proto" = private global {{.*}} section "__OBJC,__property,regular,no_dead_strip", align 8 // CHECK-FRAGILE: @"\01l_OBJC_PROTOCOLEXT_Proto" = private global %struct._objc_protocol_extension { i32 48, {{.*}} @"OBJC_$_CLASS_PROP_PROTO_LIST_Proto" {{.*}} }, align 8 // CHECK-FRAGILE: @"\01l_OBJC_$_CLASS_PROP_LIST_Foo_Category" = private global {{.*}} section "__OBJC,__property,regular,no_dead_strip", align 8 From a4e276380948b047546c383d76e245448091fd96 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Wed, 20 Apr 2016 23:59:32 +0000 Subject: [PATCH 522/742] Module Debugging: Emit the canonical debug info for Objective-C classes in the compile unit that contains their implementation even if their interface is declared in a module. The private @implementation of an @interface may have additional hidden ivars so we should not defer to the public version of the type that is found in the module. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@266937 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 29dbb7b2421c7631bf4e86948c3536700e2ca447) --- lib/CodeGen/CGDebugInfo.cpp | 7 +++++-- test/Modules/ExtDebugInfo.m | 14 +++++++++++++- test/Modules/Inputs/DebugObjC.h | 6 ++++++ test/Modules/ModuleDebugInfo.m | 5 +++++ 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index 80ce1505008..d0517957a4d 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -1687,8 +1687,11 @@ llvm::DIType *CGDebugInfo::CreateType(const ObjCInterfaceType *Ty, if (!ID) return nullptr; - // Return a forward declaration if this type was imported from a clang module. - if (DebugTypeExtRefs && ID->isFromASTFile() && ID->getDefinition()) + // Return a forward declaration if this type was imported from a clang module, + // and this is not the compile unit with the implementation of the type (which + // may contain hidden ivars). + if (DebugTypeExtRefs && ID->isFromASTFile() && ID->getDefinition() && + !ID->getImplementation()) return DBuilder.createForwardDecl(llvm::dwarf::DW_TAG_structure_type, ID->getName(), getDeclContextDescriptor(ID), Unit, 0); diff --git a/test/Modules/ExtDebugInfo.m b/test/Modules/ExtDebugInfo.m index 3b907ac729a..71ca853fb7d 100644 --- a/test/Modules/ExtDebugInfo.m +++ b/test/Modules/ExtDebugInfo.m @@ -18,6 +18,11 @@ @import DebugObjC; #endif +@implementation ObjCClassWithPrivateIVars { + int hidden_ivar; +} +@end + TypedefUnion tdu; TypedefEnum tde; TypedefStruct tds; @@ -29,9 +34,16 @@ int foo(ObjCClass *c) { return [c property]; } +// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "ObjCClassWithPrivateIVars", +// CHECK-SAME: flags: DIFlagObjcClassComplete + +// CHECK: ![[MOD:.*]] = !DIModule(scope: null, name: "DebugObjC + +// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "hidden_ivar", +// CHECK-SAME: flags: DIFlagPrivate) + // CHECK: !DIGlobalVariable(name: "GlobalUnion", // CHECK-SAME: type: ![[GLOBAL_UNION:[0-9]+]] -// CHECK: ![[MOD:.*]] = !DIModule(scope: null, name: "DebugObjC // CHECK: ![[GLOBAL_UNION]] = distinct !DICompositeType(tag: DW_TAG_union_type, // CHECK-SAME: elements: !{{[0-9]+}}) diff --git a/test/Modules/Inputs/DebugObjC.h b/test/Modules/Inputs/DebugObjC.h index eb7a9f9da9f..af1cd981ce1 100644 --- a/test/Modules/Inputs/DebugObjC.h +++ b/test/Modules/Inputs/DebugObjC.h @@ -1,3 +1,4 @@ +// -*- ObjC -*- @class FwdDecl; @interface ObjCClass { @@ -9,6 +10,11 @@ @property int property; @end +@interface ObjCClassWithPrivateIVars { + int public_ivar; +} +@end + @interface ObjCClass (Category) - categoryMethod; @end diff --git a/test/Modules/ModuleDebugInfo.m b/test/Modules/ModuleDebugInfo.m index cd01e920bbc..f41c4fc0a00 100644 --- a/test/Modules/ModuleDebugInfo.m +++ b/test/Modules/ModuleDebugInfo.m @@ -39,6 +39,7 @@ // CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "ObjCClass", // CHECK-SAME: scope: ![[MODULE]], +// CHECK-SAME: elements // The forward declaration should not be in the module scope. // CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "OpaqueData", file @@ -46,6 +47,10 @@ // CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "FwdDecl", // CHECK-SAME: scope: ![[MODULE]], +// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "ObjCClassWithPrivateIVars", +// CHECK-SAME: scope: ![[MODULE]], +// CHECK-SAME: elements + // CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "FwdDeclared" // CHECK-SAME: elements: From f17037b92c4f92d8d155a11cdb6874c3bd75b868 Mon Sep 17 00:00:00 2001 From: Michael Ilseman Date: Thu, 21 Apr 2016 15:03:37 -0700 Subject: [PATCH 523/742] [swift_newtype] Add heading to doc --- include/clang/Basic/AttrDocs.td | 1 + 1 file changed, 1 insertion(+) diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td index 2132ea3f28c..cee6ee4e7e6 100644 --- a/include/clang/Basic/AttrDocs.td +++ b/include/clang/Basic/AttrDocs.td @@ -1809,6 +1809,7 @@ All of these conventions except ``none`` require the function to have an error p def SwiftNewtypeDocs : Documentation { let Category = SwiftDocs; + let Heading = "swift_newtype"; let Content = [{ The ``swift_newtype`` attribute indicates that the typedef to which the attribute appertains is imported as a new Swift type of the typedef's name. * ``swift_newtype(struct)`` means that a Swift struct will be created for this typedef. From 291e6baacfbe8c13fbc743916b833ef56a01b07f Mon Sep 17 00:00:00 2001 From: Sean Callanan Date: Thu, 3 Mar 2016 01:21:28 +0000 Subject: [PATCH 524/742] Fixed a problem where the ASTImporter mishandled in-class initializers. Previously, the ASTImporter, when copying a FieldDecl, would make the new FieldDecl use the exact same in-class initializer as the original FieldDecl, which is a problem since the initializer is in the wrong AST. The initializer must be imported, just like all the other parts of the field. Doug Gregor reviewed this fix. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262572 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 2b2e7a1c3d62dd98ed8a274e7f91818c329539d4) --- lib/AST/ASTImporter.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 56d59952692..5c67f42d1e4 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -3052,8 +3052,13 @@ Decl *ASTNodeImporter::VisitFieldDecl(FieldDecl *D) { D->getInClassInitStyle()); ToField->setAccess(D->getAccess()); ToField->setLexicalDeclContext(LexicalDC); - if (ToField->hasInClassInitializer()) - ToField->setInClassInitializer(D->getInClassInitializer()); + if (Expr *FromInitializer = ToField->getInClassInitializer()) { + Expr *ToInitializer = Importer.Import(FromInitializer); + if (ToInitializer) + ToField->setInClassInitializer(ToInitializer); + else + return nullptr; + } ToField->setImplicit(D->isImplicit()); Importer.Imported(D, ToField); LexicalDC->addDeclInternal(ToField); From b75150c09b566fc656985227da92f2a8bfebb88f Mon Sep 17 00:00:00 2001 From: Sean Callanan Date: Thu, 3 Mar 2016 02:22:05 +0000 Subject: [PATCH 525/742] Caught and fixed a typo in r262572. I should have checked and imported D's in-class initializer. Instead I accidentally used ToField's in-class initializer, which is always NULL so ToField will never get one. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262576 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 5f4fa93f22cae6988388e2fa7235dc1d8836af56) --- lib/AST/ASTImporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 5c67f42d1e4..00cfb48e4cb 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -3052,7 +3052,7 @@ Decl *ASTNodeImporter::VisitFieldDecl(FieldDecl *D) { D->getInClassInitStyle()); ToField->setAccess(D->getAccess()); ToField->setLexicalDeclContext(LexicalDC); - if (Expr *FromInitializer = ToField->getInClassInitializer()) { + if (Expr *FromInitializer = D->getInClassInitializer()) { Expr *ToInitializer = Importer.Import(FromInitializer); if (ToInitializer) ToField->setInClassInitializer(ToInitializer); From a4fee42a8282cd32c6f77120d1edc2991e249486 Mon Sep 17 00:00:00 2001 From: Pavel Labath Date: Fri, 4 Mar 2016 10:00:08 +0000 Subject: [PATCH 526/742] [SemaExprCXX] Avoid calling isInSystemHeader for invalid source locations Summary: While diagnosing a CXXNewExpr warning, we were calling isInSystemHeader(), which expect to be called with a valid source location. This causes an assertion failure if the location is unknown. A quick grep shows it's not without precedent to guard calls to the function with a "Loc.isValid()". This fixes a test failure in LLDB, which always creates object with invalid source locations as it does not (always) have access to the source. Reviewers: nlewycky Subscribers: lldb-commits, cfe-commits Differential Revision: http://reviews.llvm.org/D17847 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262700 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit e083b17212302c0abb2b08468df7697839c0841f) --- lib/Sema/SemaExprCXX.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 32aa5a69d07..2f34ca46758 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -1551,7 +1551,8 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, // new. if (PlacementArgs.empty() && OperatorNew && (OperatorNew->isImplicit() || - getSourceManager().isInSystemHeader(OperatorNew->getLocStart()))) { + (OperatorNew->getLocStart().isValid() && + getSourceManager().isInSystemHeader(OperatorNew->getLocStart())))) { if (unsigned Align = Context.getPreferredTypeAlign(AllocType.getTypePtr())){ unsigned SuitableAlign = Context.getTargetInfo().getSuitableAlign(); if (Align > SuitableAlign) From ed2f57f6be0333e3b5a5e954d76b9c06c0375747 Mon Sep 17 00:00:00 2001 From: Richard Trieu Date: Sat, 5 Mar 2016 04:04:57 +0000 Subject: [PATCH 527/742] Add null check to diagnostic path for lambda captures. Previously, the failed capture of a variable in nested lambdas may crash when the lambda pointer is null. Only give the note if a location can be retreived from the lambda pointer. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262765 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 1585d71ce11df5799d519a6af26ae256dff633c1) --- lib/Sema/SemaExpr.cpp | 5 +++-- test/SemaCXX/lambda-expressions.cpp | 12 ++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 6eaa35a0ee5..81e396eb5d8 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -13356,8 +13356,9 @@ bool Sema::tryCaptureVariable( Diag(ExprLoc, diag::err_lambda_impcap) << Var->getDeclName(); Diag(Var->getLocation(), diag::note_previous_decl) << Var->getDeclName(); - Diag(cast(CSI)->Lambda->getLocStart(), - diag::note_lambda_decl); + if (cast(CSI)->Lambda) + Diag(cast(CSI)->Lambda->getLocStart(), + diag::note_lambda_decl); // FIXME: If we error out because an outer lambda can not implicitly // capture a variable that an inner lambda explicitly captures, we // should have the inner lambda do the explicit capture - because diff --git a/test/SemaCXX/lambda-expressions.cpp b/test/SemaCXX/lambda-expressions.cpp index 08446a0ee43..17808cef363 100644 --- a/test/SemaCXX/lambda-expressions.cpp +++ b/test/SemaCXX/lambda-expressions.cpp @@ -487,3 +487,15 @@ void foo() { }; } } + +namespace nested_lambda { +template +class S {}; + +void foo() { + const int num = 18; // expected-note {{'num' declared here}} + auto outer = []() { + auto inner = [](S &X) {}; // expected-error {{variable 'num' cannot be implicitly captured in a lambda with no capture-default specified}} + }; +} +} From 50f2c18da1f7c57b01af88408f80081cb8af62c9 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 21 Apr 2016 23:23:40 -0700 Subject: [PATCH 528/742] [API Notes] Add support for SwiftPrivate -> swift_private Fixes rdar://problem/25872038. --- include/clang/APINotes/Types.h | 9 ++++++++- lib/APINotes/APINotesFormat.h | 2 +- lib/APINotes/APINotesReader.cpp | 1 + lib/APINotes/APINotesWriter.cpp | 4 +++- lib/APINotes/APINotesYAMLCompiler.cpp | 24 +++++++++++++++++++++++- lib/Sema/SemaAPINotes.cpp | 5 +++++ test/APINotes/Inputs/roundtrip.apinotes | 16 ++++++++++++++++ 7 files changed, 57 insertions(+), 4 deletions(-) diff --git a/include/clang/APINotes/Types.h b/include/clang/APINotes/Types.h index b19eea51991..11a1e7acf54 100644 --- a/include/clang/APINotes/Types.h +++ b/include/clang/APINotes/Types.h @@ -70,16 +70,20 @@ class CommonEntityInfo { /// Whether this entity is marked unavailable in Swift. unsigned UnavailableInSwift : 1; + /// Whether this entity is considered "private" to a Swift overlay. + unsigned SwiftPrivate : 1; + /// Swift name of this entity. std::string SwiftName; - CommonEntityInfo() : Unavailable(0), UnavailableInSwift(0) { } + CommonEntityInfo() : Unavailable(0), UnavailableInSwift(0), SwiftPrivate(0) { } friend bool operator==(const CommonEntityInfo &lhs, const CommonEntityInfo &rhs) { return lhs.UnavailableMsg == rhs.UnavailableMsg && lhs.Unavailable == rhs.Unavailable && lhs.UnavailableInSwift == rhs.UnavailableInSwift && + lhs.SwiftPrivate == rhs.SwiftPrivate && lhs.SwiftName == rhs.SwiftName; } @@ -107,6 +111,9 @@ class CommonEntityInfo { } } + if (rhs.SwiftPrivate) + lhs.SwiftPrivate = true; + if (rhs.SwiftName.length() != 0 && lhs.SwiftName.length() == 0) lhs.SwiftName = rhs.SwiftName; diff --git a/lib/APINotes/APINotesFormat.h b/lib/APINotes/APINotesFormat.h index 0e8ca3368f6..c999ce16e4b 100644 --- a/lib/APINotes/APINotesFormat.h +++ b/lib/APINotes/APINotesFormat.h @@ -35,7 +35,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 11; // SwiftInferImportAsMember +const uint16_t VERSION_MINOR = 12; // SwiftPrivate using IdentifierID = Fixnum<31>; using IdentifierIDField = BCVBR<16>; diff --git a/lib/APINotes/APINotesReader.cpp b/lib/APINotes/APINotesReader.cpp index d424b39e3aa..44c628b8f28 100644 --- a/lib/APINotes/APINotesReader.cpp +++ b/lib/APINotes/APINotesReader.cpp @@ -33,6 +33,7 @@ namespace { uint8_t unavailableBits = *data++; info.Unavailable = (unavailableBits >> 1) & 0x01; info.UnavailableInSwift = unavailableBits & 0x01; + info.SwiftPrivate = (unavailableBits >> 2) & 0x01; unsigned msgLength = endian::readNext(data); info.UnavailableMsg diff --git a/lib/APINotes/APINotesWriter.cpp b/lib/APINotes/APINotesWriter.cpp index 425186ec2f7..758f6605e15 100644 --- a/lib/APINotes/APINotesWriter.cpp +++ b/lib/APINotes/APINotesWriter.cpp @@ -297,7 +297,9 @@ namespace { static void emitCommonEntityInfo(raw_ostream &out, const CommonEntityInfo &info) { endian::Writer writer(out); - writer.write(info.Unavailable << 1 | info.UnavailableInSwift); + writer.write(info.SwiftPrivate << 2 + | info.Unavailable << 1 + | info.UnavailableInSwift); writer.write(info.UnavailableMsg.size()); out.write(info.UnavailableMsg.c_str(), info.UnavailableMsg.size()); writer.write(info.SwiftName.size()); diff --git a/lib/APINotes/APINotesYAMLCompiler.cpp b/lib/APINotes/APINotesYAMLCompiler.cpp index 9d7873cee6d..310aed81518 100644 --- a/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/lib/APINotes/APINotesYAMLCompiler.cpp @@ -172,6 +172,7 @@ namespace { NullabilitySeq Nullability; llvm::Optional NullabilityOfRet; AvailabilityItem Availability; + bool SwiftPrivate = false; StringRef SwiftName; api_notes::FactoryAsInitKind FactoryAsInit = api_notes::FactoryAsInitKind::Infer; @@ -184,6 +185,7 @@ namespace { StringRef Name; llvm::Optional Nullability; AvailabilityItem Availability; + bool SwiftPrivate = false; StringRef SwiftName; }; typedef std::vector PropertiesSeq; @@ -192,6 +194,7 @@ namespace { StringRef Name; bool AuditedForNullability = false; AvailabilityItem Availability; + bool SwiftPrivate = false; StringRef SwiftName; StringRef SwiftBridge; MethodsSeq Methods; @@ -204,6 +207,7 @@ namespace { NullabilitySeq Nullability; llvm::Optional NullabilityOfRet; AvailabilityItem Availability; + bool SwiftPrivate = false; StringRef SwiftName; }; typedef std::vector FunctionsSeq; @@ -212,6 +216,7 @@ namespace { StringRef Name; llvm::Optional Nullability; AvailabilityItem Availability; + bool SwiftPrivate = false; StringRef SwiftName; }; typedef std::vector GlobalVariablesSeq; @@ -219,6 +224,7 @@ namespace { struct EnumConstant { StringRef Name; AvailabilityItem Availability; + bool SwiftPrivate = false; StringRef SwiftName; }; typedef std::vector EnumConstantsSeq; @@ -227,6 +233,7 @@ namespace { StringRef Name; AvailabilityItem Availability; StringRef SwiftName; + bool SwiftPrivate = false; StringRef SwiftBridge; }; typedef std::vector TagsSeq; @@ -235,6 +242,7 @@ namespace { StringRef Name; AvailabilityItem Availability; StringRef SwiftName; + bool SwiftPrivate = false; StringRef SwiftBridge; }; typedef std::vector TypedefsSeq; @@ -319,6 +327,7 @@ namespace llvm { AbsentNullability); io.mapOptional("Availability", p.Availability.Mode); io.mapOptional("AvailabilityMsg", p.Availability.Msg); + io.mapOptional("SwiftPrivate", p.SwiftPrivate); io.mapOptional("SwiftName", p.SwiftName); } }; @@ -333,6 +342,7 @@ namespace llvm { AbsentNullability); io.mapOptional("Availability", m.Availability.Mode); io.mapOptional("AvailabilityMsg", m.Availability.Msg); + io.mapOptional("SwiftPrivate", m.SwiftPrivate); io.mapOptional("SwiftName", m.SwiftName); io.mapOptional("FactoryAsInit", m.FactoryAsInit, api_notes::FactoryAsInitKind::Infer); @@ -348,6 +358,7 @@ namespace llvm { io.mapOptional("AuditedForNullability", c.AuditedForNullability, false); io.mapOptional("Availability", c.Availability.Mode); io.mapOptional("AvailabilityMsg", c.Availability.Msg); + io.mapOptional("SwiftPrivate", c.SwiftPrivate); io.mapOptional("SwiftName", c.SwiftName); io.mapOptional("SwiftBridge", c.SwiftBridge); io.mapOptional("Methods", c.Methods); @@ -364,6 +375,7 @@ namespace llvm { AbsentNullability); io.mapOptional("Availability", f.Availability.Mode); io.mapOptional("AvailabilityMsg", f.Availability.Msg); + io.mapOptional("SwiftPrivate", f.SwiftPrivate); io.mapOptional("SwiftName", f.SwiftName); } }; @@ -376,6 +388,7 @@ namespace llvm { AbsentNullability); io.mapOptional("Availability", v.Availability.Mode); io.mapOptional("AvailabilityMsg", v.Availability.Msg); + io.mapOptional("SwiftPrivate", v.SwiftPrivate); io.mapOptional("SwiftName", v.SwiftName); } }; @@ -386,6 +399,7 @@ namespace llvm { io.mapRequired("Name", v.Name); io.mapOptional("Availability", v.Availability.Mode); io.mapOptional("AvailabilityMsg", v.Availability.Msg); + io.mapOptional("SwiftPrivate", v.SwiftPrivate); io.mapOptional("SwiftName", v.SwiftName); } }; @@ -396,6 +410,7 @@ namespace llvm { io.mapRequired("Name", t.Name); io.mapOptional("Availability", t.Availability.Mode); io.mapOptional("AvailabilityMsg", t.Availability.Msg); + io.mapOptional("SwiftPrivate", t.SwiftPrivate); io.mapOptional("SwiftName", t.SwiftName); io.mapOptional("SwiftBridge", t.SwiftBridge); } @@ -407,6 +422,7 @@ namespace llvm { io.mapRequired("Name", t.Name); io.mapOptional("Availability", t.Availability.Mode); io.mapOptional("AvailabilityMsg", t.Availability.Msg); + io.mapOptional("SwiftPrivate", t.SwiftPrivate); io.mapOptional("SwiftName", t.SwiftName); io.mapOptional("SwiftBridge", t.SwiftBridge); } @@ -541,6 +557,7 @@ namespace { return true; convertAvailability(common.Availability, info, apiName); + info.SwiftPrivate = common.SwiftPrivate; info.SwiftName = common.SwiftName; return false; } @@ -642,6 +659,7 @@ namespace { if (!isAvailable(prop.Availability)) continue; convertAvailability(prop.Availability, pInfo, prop.Name); + pInfo.SwiftPrivate = prop.SwiftPrivate; pInfo.SwiftName = prop.SwiftName; if (prop.Nullability) pInfo.setNullabilityAudited(*prop.Nullability); @@ -696,6 +714,7 @@ namespace { if (!isAvailable(global.Availability)) continue; convertAvailability(global.Availability, info, global.Name); + info.SwiftPrivate = global.SwiftPrivate; info.SwiftName = global.SwiftName; if (global.Nullability) info.setNullabilityAudited(*global.Nullability); @@ -716,6 +735,7 @@ namespace { if (!isAvailable(function.Availability)) continue; convertAvailability(function.Availability, info, function.Name); + info.SwiftPrivate = function.SwiftPrivate; info.SwiftName = function.SwiftName; convertNullability(function.Nullability, function.NullabilityOfRet, @@ -738,6 +758,7 @@ namespace { if (!isAvailable(enumConstant.Availability)) continue; convertAvailability(enumConstant.Availability, info, enumConstant.Name); + info.SwiftPrivate = enumConstant.SwiftPrivate; info.SwiftName = enumConstant.SwiftName; Writer->addEnumConstant(enumConstant.Name, info); } @@ -747,7 +768,7 @@ namespace { for (const auto &t : TheModule.Tags) { // Check for duplicate tag definitions. if (!knownTags.insert(t.Name).second) { - emitError("multiple definitions of tag '" + t.Name + "'"); + emitError("multiple definitions Of tag '" + t.Name + "'"); continue; } @@ -859,6 +880,7 @@ namespace { template void handleCommon(T &record, const CommonEntityInfo &info) { handleAvailability(record.Availability, info); + record.SwiftPrivate = info.SwiftPrivate; record.SwiftName = copyString(info.SwiftName); } diff --git a/lib/Sema/SemaAPINotes.cpp b/lib/Sema/SemaAPINotes.cpp index 34c61b2a7fa..bc1ded14e38 100644 --- a/lib/Sema/SemaAPINotes.cpp +++ b/lib/Sema/SemaAPINotes.cpp @@ -120,6 +120,11 @@ static void ProcessAPINotes(Sema &S, Decl *D, CopyString(S.Context, Info.UnavailableMsg))); } + // swift_private + if (Info.SwiftPrivate && !D->hasAttr()) { + D->addAttr(SwiftPrivateAttr::CreateImplicit(S.Context)); + } + // swift_name if (!Info.SwiftName.empty() && !D->hasAttr()) { D->addAttr(SwiftNameAttr::CreateImplicit(S.Context, diff --git a/test/APINotes/Inputs/roundtrip.apinotes b/test/APINotes/Inputs/roundtrip.apinotes index 8509c79389b..f02aa003dc0 100644 --- a/test/APINotes/Inputs/roundtrip.apinotes +++ b/test/APINotes/Inputs/roundtrip.apinotes @@ -7,6 +7,7 @@ Classes: - Name: NSCell Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: '' SwiftBridge: '' Methods: @@ -15,6 +16,7 @@ Classes: NullabilityOfRet: U Availability: available AvailabilityMsg: '' + SwiftPrivate: true SwiftName: '' DesignatedInit: true - Selector: 'initImageCell:' @@ -23,6 +25,7 @@ Classes: NullabilityOfRet: U Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: '' DesignatedInit: true - Selector: 'initTextCell:' @@ -31,6 +34,7 @@ Classes: NullabilityOfRet: U Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: '' DesignatedInit: true - Selector: 'initWithCoder:' @@ -39,6 +43,7 @@ Classes: NullabilityOfRet: U Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: '' DesignatedInit: true Required: true @@ -46,6 +51,7 @@ Classes: AuditedForNullability: true Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: '' SwiftBridge: View Methods: @@ -55,6 +61,7 @@ Classes: NullabilityOfRet: N Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: '' - Selector: 'addSubview:positioned:relativeTo:' MethodKind: Instance @@ -62,6 +69,7 @@ Classes: NullabilityOfRet: N Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: '' - Selector: 'beginDraggingSessionWithItems:event:source:' MethodKind: Instance @@ -69,44 +77,52 @@ Classes: NullabilityOfRet: N Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: 'beginDragginSession(_:event:source:)' Properties: - Name: enclosingScrollView Nullability: O Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: enclosing - Name: makeBackingLayer Nullability: N Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: '' Functions: - Name: NSAvailableWindowDepths NullabilityOfRet: N Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: 'availableWindowDepths()' Globals: - Name: NSCalibratedWhiteColorSpace Nullability: N Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: calibratedWhite Enumerators: - Name: NSColorRed Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: Red Tags: - Name: NSSomeStruct Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: SomeStruct SwiftBridge: '' Typedefs: - Name: NSTypedef Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: Typedef SwiftBridge: '' From 31b55e881f3f5d67ab15f586525b4dd92af60c6d Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 21 Apr 2016 23:23:40 -0700 Subject: [PATCH 529/742] [API Notes] Add support for SwiftPrivate -> swift_private Fixes rdar://problem/25872038. --- include/clang/APINotes/Types.h | 9 ++++++++- lib/APINotes/APINotesFormat.h | 2 +- lib/APINotes/APINotesReader.cpp | 1 + lib/APINotes/APINotesWriter.cpp | 4 +++- lib/APINotes/APINotesYAMLCompiler.cpp | 24 +++++++++++++++++++++++- lib/Sema/SemaAPINotes.cpp | 5 +++++ test/APINotes/Inputs/roundtrip.apinotes | 16 ++++++++++++++++ 7 files changed, 57 insertions(+), 4 deletions(-) diff --git a/include/clang/APINotes/Types.h b/include/clang/APINotes/Types.h index b19eea51991..11a1e7acf54 100644 --- a/include/clang/APINotes/Types.h +++ b/include/clang/APINotes/Types.h @@ -70,16 +70,20 @@ class CommonEntityInfo { /// Whether this entity is marked unavailable in Swift. unsigned UnavailableInSwift : 1; + /// Whether this entity is considered "private" to a Swift overlay. + unsigned SwiftPrivate : 1; + /// Swift name of this entity. std::string SwiftName; - CommonEntityInfo() : Unavailable(0), UnavailableInSwift(0) { } + CommonEntityInfo() : Unavailable(0), UnavailableInSwift(0), SwiftPrivate(0) { } friend bool operator==(const CommonEntityInfo &lhs, const CommonEntityInfo &rhs) { return lhs.UnavailableMsg == rhs.UnavailableMsg && lhs.Unavailable == rhs.Unavailable && lhs.UnavailableInSwift == rhs.UnavailableInSwift && + lhs.SwiftPrivate == rhs.SwiftPrivate && lhs.SwiftName == rhs.SwiftName; } @@ -107,6 +111,9 @@ class CommonEntityInfo { } } + if (rhs.SwiftPrivate) + lhs.SwiftPrivate = true; + if (rhs.SwiftName.length() != 0 && lhs.SwiftName.length() == 0) lhs.SwiftName = rhs.SwiftName; diff --git a/lib/APINotes/APINotesFormat.h b/lib/APINotes/APINotesFormat.h index 0e8ca3368f6..c999ce16e4b 100644 --- a/lib/APINotes/APINotesFormat.h +++ b/lib/APINotes/APINotesFormat.h @@ -35,7 +35,7 @@ const uint16_t VERSION_MAJOR = 0; /// API notes file minor version number. /// /// When the format changes IN ANY WAY, this number should be incremented. -const uint16_t VERSION_MINOR = 11; // SwiftInferImportAsMember +const uint16_t VERSION_MINOR = 12; // SwiftPrivate using IdentifierID = Fixnum<31>; using IdentifierIDField = BCVBR<16>; diff --git a/lib/APINotes/APINotesReader.cpp b/lib/APINotes/APINotesReader.cpp index d424b39e3aa..44c628b8f28 100644 --- a/lib/APINotes/APINotesReader.cpp +++ b/lib/APINotes/APINotesReader.cpp @@ -33,6 +33,7 @@ namespace { uint8_t unavailableBits = *data++; info.Unavailable = (unavailableBits >> 1) & 0x01; info.UnavailableInSwift = unavailableBits & 0x01; + info.SwiftPrivate = (unavailableBits >> 2) & 0x01; unsigned msgLength = endian::readNext(data); info.UnavailableMsg diff --git a/lib/APINotes/APINotesWriter.cpp b/lib/APINotes/APINotesWriter.cpp index 425186ec2f7..758f6605e15 100644 --- a/lib/APINotes/APINotesWriter.cpp +++ b/lib/APINotes/APINotesWriter.cpp @@ -297,7 +297,9 @@ namespace { static void emitCommonEntityInfo(raw_ostream &out, const CommonEntityInfo &info) { endian::Writer writer(out); - writer.write(info.Unavailable << 1 | info.UnavailableInSwift); + writer.write(info.SwiftPrivate << 2 + | info.Unavailable << 1 + | info.UnavailableInSwift); writer.write(info.UnavailableMsg.size()); out.write(info.UnavailableMsg.c_str(), info.UnavailableMsg.size()); writer.write(info.SwiftName.size()); diff --git a/lib/APINotes/APINotesYAMLCompiler.cpp b/lib/APINotes/APINotesYAMLCompiler.cpp index 9d7873cee6d..310aed81518 100644 --- a/lib/APINotes/APINotesYAMLCompiler.cpp +++ b/lib/APINotes/APINotesYAMLCompiler.cpp @@ -172,6 +172,7 @@ namespace { NullabilitySeq Nullability; llvm::Optional NullabilityOfRet; AvailabilityItem Availability; + bool SwiftPrivate = false; StringRef SwiftName; api_notes::FactoryAsInitKind FactoryAsInit = api_notes::FactoryAsInitKind::Infer; @@ -184,6 +185,7 @@ namespace { StringRef Name; llvm::Optional Nullability; AvailabilityItem Availability; + bool SwiftPrivate = false; StringRef SwiftName; }; typedef std::vector PropertiesSeq; @@ -192,6 +194,7 @@ namespace { StringRef Name; bool AuditedForNullability = false; AvailabilityItem Availability; + bool SwiftPrivate = false; StringRef SwiftName; StringRef SwiftBridge; MethodsSeq Methods; @@ -204,6 +207,7 @@ namespace { NullabilitySeq Nullability; llvm::Optional NullabilityOfRet; AvailabilityItem Availability; + bool SwiftPrivate = false; StringRef SwiftName; }; typedef std::vector FunctionsSeq; @@ -212,6 +216,7 @@ namespace { StringRef Name; llvm::Optional Nullability; AvailabilityItem Availability; + bool SwiftPrivate = false; StringRef SwiftName; }; typedef std::vector GlobalVariablesSeq; @@ -219,6 +224,7 @@ namespace { struct EnumConstant { StringRef Name; AvailabilityItem Availability; + bool SwiftPrivate = false; StringRef SwiftName; }; typedef std::vector EnumConstantsSeq; @@ -227,6 +233,7 @@ namespace { StringRef Name; AvailabilityItem Availability; StringRef SwiftName; + bool SwiftPrivate = false; StringRef SwiftBridge; }; typedef std::vector TagsSeq; @@ -235,6 +242,7 @@ namespace { StringRef Name; AvailabilityItem Availability; StringRef SwiftName; + bool SwiftPrivate = false; StringRef SwiftBridge; }; typedef std::vector TypedefsSeq; @@ -319,6 +327,7 @@ namespace llvm { AbsentNullability); io.mapOptional("Availability", p.Availability.Mode); io.mapOptional("AvailabilityMsg", p.Availability.Msg); + io.mapOptional("SwiftPrivate", p.SwiftPrivate); io.mapOptional("SwiftName", p.SwiftName); } }; @@ -333,6 +342,7 @@ namespace llvm { AbsentNullability); io.mapOptional("Availability", m.Availability.Mode); io.mapOptional("AvailabilityMsg", m.Availability.Msg); + io.mapOptional("SwiftPrivate", m.SwiftPrivate); io.mapOptional("SwiftName", m.SwiftName); io.mapOptional("FactoryAsInit", m.FactoryAsInit, api_notes::FactoryAsInitKind::Infer); @@ -348,6 +358,7 @@ namespace llvm { io.mapOptional("AuditedForNullability", c.AuditedForNullability, false); io.mapOptional("Availability", c.Availability.Mode); io.mapOptional("AvailabilityMsg", c.Availability.Msg); + io.mapOptional("SwiftPrivate", c.SwiftPrivate); io.mapOptional("SwiftName", c.SwiftName); io.mapOptional("SwiftBridge", c.SwiftBridge); io.mapOptional("Methods", c.Methods); @@ -364,6 +375,7 @@ namespace llvm { AbsentNullability); io.mapOptional("Availability", f.Availability.Mode); io.mapOptional("AvailabilityMsg", f.Availability.Msg); + io.mapOptional("SwiftPrivate", f.SwiftPrivate); io.mapOptional("SwiftName", f.SwiftName); } }; @@ -376,6 +388,7 @@ namespace llvm { AbsentNullability); io.mapOptional("Availability", v.Availability.Mode); io.mapOptional("AvailabilityMsg", v.Availability.Msg); + io.mapOptional("SwiftPrivate", v.SwiftPrivate); io.mapOptional("SwiftName", v.SwiftName); } }; @@ -386,6 +399,7 @@ namespace llvm { io.mapRequired("Name", v.Name); io.mapOptional("Availability", v.Availability.Mode); io.mapOptional("AvailabilityMsg", v.Availability.Msg); + io.mapOptional("SwiftPrivate", v.SwiftPrivate); io.mapOptional("SwiftName", v.SwiftName); } }; @@ -396,6 +410,7 @@ namespace llvm { io.mapRequired("Name", t.Name); io.mapOptional("Availability", t.Availability.Mode); io.mapOptional("AvailabilityMsg", t.Availability.Msg); + io.mapOptional("SwiftPrivate", t.SwiftPrivate); io.mapOptional("SwiftName", t.SwiftName); io.mapOptional("SwiftBridge", t.SwiftBridge); } @@ -407,6 +422,7 @@ namespace llvm { io.mapRequired("Name", t.Name); io.mapOptional("Availability", t.Availability.Mode); io.mapOptional("AvailabilityMsg", t.Availability.Msg); + io.mapOptional("SwiftPrivate", t.SwiftPrivate); io.mapOptional("SwiftName", t.SwiftName); io.mapOptional("SwiftBridge", t.SwiftBridge); } @@ -541,6 +557,7 @@ namespace { return true; convertAvailability(common.Availability, info, apiName); + info.SwiftPrivate = common.SwiftPrivate; info.SwiftName = common.SwiftName; return false; } @@ -642,6 +659,7 @@ namespace { if (!isAvailable(prop.Availability)) continue; convertAvailability(prop.Availability, pInfo, prop.Name); + pInfo.SwiftPrivate = prop.SwiftPrivate; pInfo.SwiftName = prop.SwiftName; if (prop.Nullability) pInfo.setNullabilityAudited(*prop.Nullability); @@ -696,6 +714,7 @@ namespace { if (!isAvailable(global.Availability)) continue; convertAvailability(global.Availability, info, global.Name); + info.SwiftPrivate = global.SwiftPrivate; info.SwiftName = global.SwiftName; if (global.Nullability) info.setNullabilityAudited(*global.Nullability); @@ -716,6 +735,7 @@ namespace { if (!isAvailable(function.Availability)) continue; convertAvailability(function.Availability, info, function.Name); + info.SwiftPrivate = function.SwiftPrivate; info.SwiftName = function.SwiftName; convertNullability(function.Nullability, function.NullabilityOfRet, @@ -738,6 +758,7 @@ namespace { if (!isAvailable(enumConstant.Availability)) continue; convertAvailability(enumConstant.Availability, info, enumConstant.Name); + info.SwiftPrivate = enumConstant.SwiftPrivate; info.SwiftName = enumConstant.SwiftName; Writer->addEnumConstant(enumConstant.Name, info); } @@ -747,7 +768,7 @@ namespace { for (const auto &t : TheModule.Tags) { // Check for duplicate tag definitions. if (!knownTags.insert(t.Name).second) { - emitError("multiple definitions of tag '" + t.Name + "'"); + emitError("multiple definitions Of tag '" + t.Name + "'"); continue; } @@ -859,6 +880,7 @@ namespace { template void handleCommon(T &record, const CommonEntityInfo &info) { handleAvailability(record.Availability, info); + record.SwiftPrivate = info.SwiftPrivate; record.SwiftName = copyString(info.SwiftName); } diff --git a/lib/Sema/SemaAPINotes.cpp b/lib/Sema/SemaAPINotes.cpp index bef88e2f872..5072824313e 100644 --- a/lib/Sema/SemaAPINotes.cpp +++ b/lib/Sema/SemaAPINotes.cpp @@ -122,6 +122,11 @@ static void ProcessAPINotes(Sema &S, Decl *D, /*Replacement=*/StringRef())); } + // swift_private + if (Info.SwiftPrivate && !D->hasAttr()) { + D->addAttr(SwiftPrivateAttr::CreateImplicit(S.Context)); + } + // swift_name if (!Info.SwiftName.empty() && !D->hasAttr()) { D->addAttr(SwiftNameAttr::CreateImplicit(S.Context, diff --git a/test/APINotes/Inputs/roundtrip.apinotes b/test/APINotes/Inputs/roundtrip.apinotes index 8509c79389b..f02aa003dc0 100644 --- a/test/APINotes/Inputs/roundtrip.apinotes +++ b/test/APINotes/Inputs/roundtrip.apinotes @@ -7,6 +7,7 @@ Classes: - Name: NSCell Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: '' SwiftBridge: '' Methods: @@ -15,6 +16,7 @@ Classes: NullabilityOfRet: U Availability: available AvailabilityMsg: '' + SwiftPrivate: true SwiftName: '' DesignatedInit: true - Selector: 'initImageCell:' @@ -23,6 +25,7 @@ Classes: NullabilityOfRet: U Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: '' DesignatedInit: true - Selector: 'initTextCell:' @@ -31,6 +34,7 @@ Classes: NullabilityOfRet: U Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: '' DesignatedInit: true - Selector: 'initWithCoder:' @@ -39,6 +43,7 @@ Classes: NullabilityOfRet: U Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: '' DesignatedInit: true Required: true @@ -46,6 +51,7 @@ Classes: AuditedForNullability: true Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: '' SwiftBridge: View Methods: @@ -55,6 +61,7 @@ Classes: NullabilityOfRet: N Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: '' - Selector: 'addSubview:positioned:relativeTo:' MethodKind: Instance @@ -62,6 +69,7 @@ Classes: NullabilityOfRet: N Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: '' - Selector: 'beginDraggingSessionWithItems:event:source:' MethodKind: Instance @@ -69,44 +77,52 @@ Classes: NullabilityOfRet: N Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: 'beginDragginSession(_:event:source:)' Properties: - Name: enclosingScrollView Nullability: O Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: enclosing - Name: makeBackingLayer Nullability: N Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: '' Functions: - Name: NSAvailableWindowDepths NullabilityOfRet: N Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: 'availableWindowDepths()' Globals: - Name: NSCalibratedWhiteColorSpace Nullability: N Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: calibratedWhite Enumerators: - Name: NSColorRed Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: Red Tags: - Name: NSSomeStruct Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: SomeStruct SwiftBridge: '' Typedefs: - Name: NSTypedef Availability: available AvailabilityMsg: '' + SwiftPrivate: false SwiftName: Typedef SwiftBridge: '' From 992a0072d89e4b198567339d7f39f930b666ef3c Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Mon, 15 Feb 2016 00:36:52 +0000 Subject: [PATCH 530/742] silence -Wreturn-type warnings These codepaths would generate warnings with GCC on linux even though the switch was covered. Add llvm_unreachable markers to indicate that the switch should be covered. NFC. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@260865 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Index/IndexSymbol.cpp | 3 +++ lib/Index/IndexingContext.cpp | 1 + tools/libclang/CXIndexDataConsumer.cpp | 3 +++ 3 files changed, 7 insertions(+) diff --git a/lib/Index/IndexSymbol.cpp b/lib/Index/IndexSymbol.cpp index 8465d3baff1..9f91e6274d4 100644 --- a/lib/Index/IndexSymbol.cpp +++ b/lib/Index/IndexSymbol.cpp @@ -289,6 +289,7 @@ StringRef index::getSymbolKindString(SymbolKind K) { case SymbolKind::Destructor: return "destructor"; case SymbolKind::ConversionFunction: return "coversion-func"; } + llvm_unreachable("invalid symbol kind"); } StringRef index::getTemplateKindStr(SymbolCXXTemplateKind TK) { @@ -298,6 +299,7 @@ StringRef index::getTemplateKindStr(SymbolCXXTemplateKind TK) { case SymbolCXXTemplateKind::TemplatePartialSpecialization : return "TPS"; case SymbolCXXTemplateKind::TemplateSpecialization: return "TS"; } + llvm_unreachable("invalid template kind"); } StringRef index::getSymbolLanguageString(SymbolLanguage K) { @@ -306,4 +308,5 @@ StringRef index::getSymbolLanguageString(SymbolLanguage K) { case SymbolLanguage::ObjC: return "ObjC"; case SymbolLanguage::CXX: return "C++"; } + llvm_unreachable("invalid symbol language kind"); } diff --git a/lib/Index/IndexingContext.cpp b/lib/Index/IndexingContext.cpp index 5c2cb246737..3d1d9902dd6 100644 --- a/lib/Index/IndexingContext.cpp +++ b/lib/Index/IndexingContext.cpp @@ -143,6 +143,7 @@ bool IndexingContext::isTemplateImplicitInstantiation(const Decl *D) { case TSK_ExplicitInstantiationDefinition: return true; } + llvm_unreachable("invalid TemplateSpecializationKind"); } bool IndexingContext::shouldIgnoreIfImplicit(const Decl *D) { diff --git a/tools/libclang/CXIndexDataConsumer.cpp b/tools/libclang/CXIndexDataConsumer.cpp index e2edad2e1b4..bc19d53aeac 100644 --- a/tools/libclang/CXIndexDataConsumer.cpp +++ b/tools/libclang/CXIndexDataConsumer.cpp @@ -1281,6 +1281,7 @@ static CXIdxEntityKind getEntityKindFromSymbolKind(SymbolKind K, SymbolLanguage case SymbolKind::Destructor: return CXIdxEntity_CXXDestructor; case SymbolKind::ConversionFunction: return CXIdxEntity_CXXConversionFunction; } + llvm_unreachable("invalid symbol kind"); } static CXIdxEntityCXXTemplateKind @@ -1293,6 +1294,7 @@ getEntityKindFromSymbolCXXTemplateKind(SymbolCXXTemplateKind K) { case SymbolCXXTemplateKind::TemplateSpecialization: return CXIdxEntity_TemplateSpecialization; } + llvm_unreachable("invalid template kind"); } static CXIdxEntityLanguage getEntityLangFromSymbolLang(SymbolLanguage L) { @@ -1301,4 +1303,5 @@ static CXIdxEntityLanguage getEntityLangFromSymbolLang(SymbolLanguage L) { case SymbolLanguage::ObjC: return CXIdxEntityLang_ObjC; case SymbolLanguage::CXX: return CXIdxEntityLang_CXX; } + llvm_unreachable("invalid symbol language"); } From e28ae67845abf581c2813d0649aa783dbec2956d Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Fri, 22 Apr 2016 07:21:04 +0000 Subject: [PATCH 531/742] [index] Change SymbolCXXTemplateKind to a 'SymbolSubKinds' bitset. This provides a more general and flexible way to annotate special symbols. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@267116 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Index/IndexSymbol.h | 18 +++++--- lib/Index/IndexSymbol.cpp | 60 ++++++++++++++++++-------- tools/c-index-test/core_main.cpp | 5 ++- tools/libclang/CXIndexDataConsumer.cpp | 19 ++++---- 4 files changed, 63 insertions(+), 39 deletions(-) diff --git a/include/clang/Index/IndexSymbol.h b/include/clang/Index/IndexSymbol.h index 56b12fa6bec..68d97d75ac2 100644 --- a/include/clang/Index/IndexSymbol.h +++ b/include/clang/Index/IndexSymbol.h @@ -59,12 +59,13 @@ enum class SymbolLanguage { CXX, }; -enum class SymbolCXXTemplateKind { - NonTemplate, - Template, - TemplatePartialSpecialization, - TemplateSpecialization, +enum class SymbolSubKind : uint8_t { + Generic = 1 << 0, + TemplatePartialSpecialization = 1 << 1, + TemplateSpecialization = 1 << 2, }; +static const unsigned SymbolSubKindBitNum = 3; +typedef unsigned SymbolSubKindSet; /// Set of roles that are attributed to symbol occurrences. enum class SymbolRole : uint16_t { @@ -99,7 +100,7 @@ struct SymbolRelation { struct SymbolInfo { SymbolKind Kind; - SymbolCXXTemplateKind TemplateKind; + SymbolSubKindSet SubKinds; SymbolLanguage Lang; }; @@ -113,9 +114,12 @@ void printSymbolRoles(SymbolRoleSet Roles, raw_ostream &OS); bool printSymbolName(const Decl *D, const LangOptions &LO, raw_ostream &OS); StringRef getSymbolKindString(SymbolKind K); -StringRef getTemplateKindStr(SymbolCXXTemplateKind TK); StringRef getSymbolLanguageString(SymbolLanguage K); +void applyForEachSymbolSubKind(SymbolSubKindSet SubKinds, + llvm::function_ref Fn); +void printSymbolSubKinds(SymbolSubKindSet SubKinds, raw_ostream &OS); + } // namespace index } // namespace clang diff --git a/lib/Index/IndexSymbol.cpp b/lib/Index/IndexSymbol.cpp index 9f91e6274d4..097aaf0721f 100644 --- a/lib/Index/IndexSymbol.cpp +++ b/lib/Index/IndexSymbol.cpp @@ -20,7 +20,7 @@ SymbolInfo index::getSymbolInfo(const Decl *D) { assert(D); SymbolInfo Info; Info.Kind = SymbolKind::Unknown; - Info.TemplateKind = SymbolCXXTemplateKind::NonTemplate; + Info.SubKinds = SymbolSubKindSet(); Info.Lang = SymbolLanguage::C; if (const TagDecl *TD = dyn_cast(D)) { @@ -46,9 +46,11 @@ SymbolInfo index::getSymbolInfo(const Decl *D) { Info.Lang = SymbolLanguage::CXX; if (isa(D)) { - Info.TemplateKind = SymbolCXXTemplateKind::TemplatePartialSpecialization; + Info.SubKinds |= (unsigned)SymbolSubKind::Generic; + Info.SubKinds |= (unsigned)SymbolSubKind::TemplatePartialSpecialization; } else if (isa(D)) { - Info.TemplateKind = SymbolCXXTemplateKind::TemplateSpecialization; + Info.SubKinds |= (unsigned)SymbolSubKind::Generic; + Info.SubKinds |= (unsigned)SymbolSubKind::TemplateSpecialization; } } else { @@ -141,12 +143,12 @@ SymbolInfo index::getSymbolInfo(const Decl *D) { } case Decl::ClassTemplate: Info.Kind = SymbolKind::Class; - Info.TemplateKind = SymbolCXXTemplateKind::Template; + Info.SubKinds |= (unsigned)SymbolSubKind::Generic; Info.Lang = SymbolLanguage::CXX; break; case Decl::FunctionTemplate: Info.Kind = SymbolKind::Function; - Info.TemplateKind = SymbolCXXTemplateKind::Template; + Info.SubKinds |= (unsigned)SymbolSubKind::Generic; Info.Lang = SymbolLanguage::CXX; if (const CXXMethodDecl *MD = dyn_cast_or_null( cast(D)->getTemplatedDecl())) { @@ -167,7 +169,7 @@ SymbolInfo index::getSymbolInfo(const Decl *D) { case Decl::TypeAliasTemplate: Info.Kind = SymbolKind::TypeAlias; Info.Lang = SymbolLanguage::CXX; - Info.TemplateKind = SymbolCXXTemplateKind::Template; + Info.SubKinds |= (unsigned)SymbolSubKind::Generic; break; case Decl::TypeAlias: Info.Kind = SymbolKind::TypeAlias; @@ -183,11 +185,13 @@ SymbolInfo index::getSymbolInfo(const Decl *D) { if (const FunctionDecl *FD = dyn_cast(D)) { if (FD->getTemplatedKind() == - FunctionDecl::TK_FunctionTemplateSpecialization) - Info.TemplateKind = SymbolCXXTemplateKind::TemplateSpecialization; + FunctionDecl::TK_FunctionTemplateSpecialization) { + Info.SubKinds |= (unsigned)SymbolSubKind::Generic; + Info.SubKinds |= (unsigned)SymbolSubKind::TemplateSpecialization; + } } - if (Info.TemplateKind != SymbolCXXTemplateKind::NonTemplate) + if (Info.SubKinds & (unsigned)SymbolSubKind::Generic) Info.Lang = SymbolLanguage::CXX; return Info; @@ -292,16 +296,6 @@ StringRef index::getSymbolKindString(SymbolKind K) { llvm_unreachable("invalid symbol kind"); } -StringRef index::getTemplateKindStr(SymbolCXXTemplateKind TK) { - switch (TK) { - case SymbolCXXTemplateKind::NonTemplate: return "NT"; - case SymbolCXXTemplateKind::Template : return "T"; - case SymbolCXXTemplateKind::TemplatePartialSpecialization : return "TPS"; - case SymbolCXXTemplateKind::TemplateSpecialization: return "TS"; - } - llvm_unreachable("invalid template kind"); -} - StringRef index::getSymbolLanguageString(SymbolLanguage K) { switch (K) { case SymbolLanguage::C: return "C"; @@ -310,3 +304,31 @@ StringRef index::getSymbolLanguageString(SymbolLanguage K) { } llvm_unreachable("invalid symbol language kind"); } + +void index::applyForEachSymbolSubKind(SymbolSubKindSet SubKinds, + llvm::function_ref Fn) { +#define APPLY_FOR_SUBKIND(K) \ + if (SubKinds & (unsigned)SymbolSubKind::K) \ + Fn(SymbolSubKind::K) + + APPLY_FOR_SUBKIND(Generic); + APPLY_FOR_SUBKIND(TemplatePartialSpecialization); + APPLY_FOR_SUBKIND(TemplateSpecialization); + +#undef APPLY_FOR_SUBKIND +} + +void index::printSymbolSubKinds(SymbolSubKindSet SubKinds, raw_ostream &OS) { + bool VisitedOnce = false; + applyForEachSymbolSubKind(SubKinds, [&](SymbolSubKind SubKind) { + if (VisitedOnce) + OS << ','; + else + VisitedOnce = true; + switch (SubKind) { + case SymbolSubKind::Generic: OS << "Gen"; break; + case SymbolSubKind::TemplatePartialSpecialization: OS << "TPS"; break; + case SymbolSubKind::TemplateSpecialization: OS << "TS"; break; + } + }); +} diff --git a/tools/c-index-test/core_main.cpp b/tools/c-index-test/core_main.cpp index 220160d8dc2..2823a9bc484 100644 --- a/tools/c-index-test/core_main.cpp +++ b/tools/c-index-test/core_main.cpp @@ -169,8 +169,9 @@ static bool printSourceSymbols(ArrayRef Args) { static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS) { OS << getSymbolKindString(SymInfo.Kind); - if (SymInfo.TemplateKind != SymbolCXXTemplateKind::NonTemplate) { - OS << '-' << getTemplateKindStr(SymInfo.TemplateKind); + if (SymInfo.SubKinds) { + OS << '-'; + printSymbolSubKinds(SymInfo.SubKinds, OS); } OS << '/' << getSymbolLanguageString(SymInfo.Lang); } diff --git a/tools/libclang/CXIndexDataConsumer.cpp b/tools/libclang/CXIndexDataConsumer.cpp index bc19d53aeac..7583c0a300c 100644 --- a/tools/libclang/CXIndexDataConsumer.cpp +++ b/tools/libclang/CXIndexDataConsumer.cpp @@ -1128,7 +1128,7 @@ void CXIndexDataConsumer::translateLoc(SourceLocation Loc, static CXIdxEntityKind getEntityKindFromSymbolKind(SymbolKind K, SymbolLanguage L); static CXIdxEntityCXXTemplateKind -getEntityKindFromSymbolCXXTemplateKind(SymbolCXXTemplateKind K); +getEntityKindFromSymbolSubKinds(SymbolSubKindSet K); static CXIdxEntityLanguage getEntityLangFromSymbolLang(SymbolLanguage L); void CXIndexDataConsumer::getEntityInfo(const NamedDecl *D, @@ -1144,8 +1144,7 @@ void CXIndexDataConsumer::getEntityInfo(const NamedDecl *D, SymbolInfo SymInfo = getSymbolInfo(D); EntityInfo.kind = getEntityKindFromSymbolKind(SymInfo.Kind, SymInfo.Lang); - EntityInfo.templateKind = - getEntityKindFromSymbolCXXTemplateKind(SymInfo.TemplateKind); + EntityInfo.templateKind = getEntityKindFromSymbolSubKinds(SymInfo.SubKinds); EntityInfo.lang = getEntityLangFromSymbolLang(SymInfo.Lang); if (D->hasAttrs()) { @@ -1285,16 +1284,14 @@ static CXIdxEntityKind getEntityKindFromSymbolKind(SymbolKind K, SymbolLanguage } static CXIdxEntityCXXTemplateKind -getEntityKindFromSymbolCXXTemplateKind(SymbolCXXTemplateKind K) { - switch (K) { - case SymbolCXXTemplateKind::NonTemplate: return CXIdxEntity_NonTemplate; - case SymbolCXXTemplateKind::Template: return CXIdxEntity_Template; - case SymbolCXXTemplateKind::TemplatePartialSpecialization: +getEntityKindFromSymbolSubKinds(SymbolSubKindSet K) { + if (K & (unsigned)SymbolSubKind::TemplatePartialSpecialization) return CXIdxEntity_TemplatePartialSpecialization; - case SymbolCXXTemplateKind::TemplateSpecialization: + if (K & (unsigned)SymbolSubKind::TemplateSpecialization) return CXIdxEntity_TemplateSpecialization; - } - llvm_unreachable("invalid template kind"); + if (K & (unsigned)SymbolSubKind::Generic) + return CXIdxEntity_Template; + return CXIdxEntity_NonTemplate; } static CXIdxEntityLanguage getEntityLangFromSymbolLang(SymbolLanguage L) { From a2d29b91ab9b65096c8ffbdf4b83c74698e26f1e Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Fri, 22 Apr 2016 07:21:10 +0000 Subject: [PATCH 532/742] [index] Add a SymbolSubKind for an ObjC unit test. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@267117 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Index/IndexSymbol.h | 3 ++- lib/Index/IndexSymbol.cpp | 36 ++++++++++++++++++++++++++++++- test/Index/Core/index-subkinds.m | 36 +++++++++++++++++++++++++++++++ tools/c-index-test/core_main.cpp | 3 ++- 4 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 test/Index/Core/index-subkinds.m diff --git a/include/clang/Index/IndexSymbol.h b/include/clang/Index/IndexSymbol.h index 68d97d75ac2..bd19771a188 100644 --- a/include/clang/Index/IndexSymbol.h +++ b/include/clang/Index/IndexSymbol.h @@ -63,8 +63,9 @@ enum class SymbolSubKind : uint8_t { Generic = 1 << 0, TemplatePartialSpecialization = 1 << 1, TemplateSpecialization = 1 << 2, + UnitTest = 1 << 3, }; -static const unsigned SymbolSubKindBitNum = 3; +static const unsigned SymbolSubKindBitNum = 4; typedef unsigned SymbolSubKindSet; /// Set of roles that are attributed to symbol occurrences. diff --git a/lib/Index/IndexSymbol.cpp b/lib/Index/IndexSymbol.cpp index 097aaf0721f..ba83d0cdef6 100644 --- a/lib/Index/IndexSymbol.cpp +++ b/lib/Index/IndexSymbol.cpp @@ -16,6 +16,30 @@ using namespace clang; using namespace clang::index; +/// \returns true if \c D is a subclass of 'XCTestCase'. +static bool isUnitTestCase(const ObjCInterfaceDecl *D) { + if (!D) + return false; + while (const ObjCInterfaceDecl *SuperD = D->getSuperClass()) { + if (SuperD->getName() == "XCTestCase") + return true; + D = SuperD; + } + return false; +} + +/// \returns true if \c D is in a subclass of 'XCTestCase', returns void, has +/// no parameters, and its name starts with 'test'. +static bool isUnitTest(const ObjCMethodDecl *D) { + if (!D->parameters().empty()) + return false; + if (!D->getReturnType()->isVoidType()) + return false; + if (!D->getSelector().getNameForSlot(0).startswith("test")) + return false; + return isUnitTestCase(D->getClassInterface()); +} + SymbolInfo index::getSymbolInfo(const Decl *D) { assert(D); SymbolInfo Info; @@ -84,10 +108,16 @@ SymbolInfo index::getSymbolInfo(const Decl *D) { case Decl::EnumConstant: Info.Kind = SymbolKind::EnumConstant; break; case Decl::ObjCInterface: - case Decl::ObjCImplementation: + case Decl::ObjCImplementation: { Info.Kind = SymbolKind::Class; Info.Lang = SymbolLanguage::ObjC; + const ObjCInterfaceDecl *ClsD = dyn_cast(D); + if (!ClsD) + ClsD = cast(D)->getClassInterface(); + if (isUnitTestCase(ClsD)) + Info.SubKinds |= (unsigned)SymbolSubKind::UnitTest; break; + } case Decl::ObjCProtocol: Info.Kind = SymbolKind::Protocol; Info.Lang = SymbolLanguage::ObjC; @@ -103,6 +133,8 @@ SymbolInfo index::getSymbolInfo(const Decl *D) { else Info.Kind = SymbolKind::ClassMethod; Info.Lang = SymbolLanguage::ObjC; + if (isUnitTest(cast(D))) + Info.SubKinds |= (unsigned)SymbolSubKind::UnitTest; break; case Decl::ObjCProperty: Info.Kind = SymbolKind::InstanceProperty; @@ -314,6 +346,7 @@ void index::applyForEachSymbolSubKind(SymbolSubKindSet SubKinds, APPLY_FOR_SUBKIND(Generic); APPLY_FOR_SUBKIND(TemplatePartialSpecialization); APPLY_FOR_SUBKIND(TemplateSpecialization); + APPLY_FOR_SUBKIND(UnitTest); #undef APPLY_FOR_SUBKIND } @@ -329,6 +362,7 @@ void index::printSymbolSubKinds(SymbolSubKindSet SubKinds, raw_ostream &OS) { case SymbolSubKind::Generic: OS << "Gen"; break; case SymbolSubKind::TemplatePartialSpecialization: OS << "TPS"; break; case SymbolSubKind::TemplateSpecialization: OS << "TS"; break; + case SymbolSubKind::UnitTest: OS << "test"; break; } }); } diff --git a/test/Index/Core/index-subkinds.m b/test/Index/Core/index-subkinds.m new file mode 100644 index 00000000000..a78e1235a3a --- /dev/null +++ b/test/Index/Core/index-subkinds.m @@ -0,0 +1,36 @@ +// RUN: c-index-test core -print-source-symbols -- %s -target x86_64-apple-macosx10.7 | FileCheck %s + +// CHECK: [[@LINE+1]]:12 | class/ObjC | XCTestCase | c:objc(cs)XCTestCase | _OBJC_CLASS_$_XCTestCase | Decl | rel: 0 +@interface XCTestCase +@end + +// CHECK: [[@LINE+1]]:12 | class(test)/ObjC | MyTestCase | c:objc(cs)MyTestCase | _OBJC_CLASS_$_MyTestCase | Decl | rel: 0 +@interface MyTestCase : XCTestCase +@end +// CHECK: [[@LINE+1]]:17 | class(test)/ObjC | MyTestCase | c:objc(cs)MyTestCase | | Def | rel: 0 +@implementation MyTestCase +// CHECK: [[@LINE+1]]:1 | instance-method(test)/ObjC | testMe | c:objc(cs)MyTestCase(im)testMe | -[MyTestCase testMe] | Def,Dyn,RelChild | rel: 1 +-(void)testMe {} +// CHECK: [[@LINE+1]]:1 | instance-method/ObjC | testResult | c:objc(cs)MyTestCase(im)testResult | -[MyTestCase testResult] | Def,Dyn,RelChild | rel: 1 +-(id)testResult { return 0; } +// CHECK: [[@LINE+1]]:1 | instance-method/ObjC | testWithInt: | c:objc(cs)MyTestCase(im)testWithInt: | -[MyTestCase testWithInt:] | Def,Dyn,RelChild | rel: 1 +-(void)testWithInt:(int)i {} +@end + +// CHECK: [[@LINE+1]]:12 | class(test)/ObjC | SubTestCase | c:objc(cs)SubTestCase | _OBJC_CLASS_$_SubTestCase | Decl | rel: 0 +@interface SubTestCase : MyTestCase +@end +// CHECK: [[@LINE+1]]:17 | class(test)/ObjC | SubTestCase | c:objc(cs)SubTestCase | | Def | rel: 0 +@implementation SubTestCase +// CHECK: [[@LINE+1]]:1 | instance-method(test)/ObjC | testIt2 | c:objc(cs)SubTestCase(im)testIt2 | -[SubTestCase testIt2] | Def,Dyn,RelChild | rel: 1 +-(void)testIt2 {} +@end + +// CHECK: [[@LINE+1]]:12 | extension/ObjC | cat | c:objc(cy)MyTestCase@cat | | Decl | rel: 0 +@interface MyTestCase(cat) +@end +// CHECK: [[@LINE+1]]:17 | extension/ObjC | MyTestCase | c:objc(cy)MyTestCase@cat | | Def | rel: 0 +@implementation MyTestCase(cat) +// CHECK: [[@LINE+1]]:1 | instance-method(test)/ObjC | testInCat | c:objc(cs)MyTestCase(im)testInCat | -[MyTestCase(cat) testInCat] | Def,Dyn,RelChild | rel: 1 +- (void)testInCat {} +@end diff --git a/tools/c-index-test/core_main.cpp b/tools/c-index-test/core_main.cpp index 2823a9bc484..10233ec786c 100644 --- a/tools/c-index-test/core_main.cpp +++ b/tools/c-index-test/core_main.cpp @@ -170,8 +170,9 @@ static bool printSourceSymbols(ArrayRef Args) { static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS) { OS << getSymbolKindString(SymInfo.Kind); if (SymInfo.SubKinds) { - OS << '-'; + OS << '('; printSymbolSubKinds(SymInfo.SubKinds, OS); + OS << ')'; } OS << '/' << getSymbolLanguageString(SymInfo.Lang); } From 1e6cba3ce39043326f4278271a970f63322ba724 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Fri, 22 Apr 2016 07:21:16 +0000 Subject: [PATCH 533/742] [index] Add SymbolSubKinds for ObjC IB annotations. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@267118 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Index/IndexSymbol.h | 4 +++- lib/Index/IndexSymbol.cpp | 17 +++++++++++++++++ test/Index/Core/index-subkinds.m | 12 ++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/include/clang/Index/IndexSymbol.h b/include/clang/Index/IndexSymbol.h index bd19771a188..b0bc93e464b 100644 --- a/include/clang/Index/IndexSymbol.h +++ b/include/clang/Index/IndexSymbol.h @@ -64,8 +64,10 @@ enum class SymbolSubKind : uint8_t { TemplatePartialSpecialization = 1 << 1, TemplateSpecialization = 1 << 2, UnitTest = 1 << 3, + IBAnnotated = 1 << 4, + IBOutletCollection = 1 << 5, }; -static const unsigned SymbolSubKindBitNum = 4; +static const unsigned SymbolSubKindBitNum = 6; typedef unsigned SymbolSubKindSet; /// Set of roles that are attributed to symbol occurrences. diff --git a/lib/Index/IndexSymbol.cpp b/lib/Index/IndexSymbol.cpp index ba83d0cdef6..13a84523007 100644 --- a/lib/Index/IndexSymbol.cpp +++ b/lib/Index/IndexSymbol.cpp @@ -40,6 +40,15 @@ static bool isUnitTest(const ObjCMethodDecl *D) { return isUnitTestCase(D->getClassInterface()); } +static void checkForIBOutlets(const Decl *D, SymbolSubKindSet &SubKindSet) { + if (D->hasAttr()) { + SubKindSet |= (unsigned)SymbolSubKind::IBAnnotated; + } else if (D->hasAttr()) { + SubKindSet |= (unsigned)SymbolSubKind::IBAnnotated; + SubKindSet |= (unsigned)SymbolSubKind::IBOutletCollection; + } +} + SymbolInfo index::getSymbolInfo(const Decl *D) { assert(D); SymbolInfo Info; @@ -135,14 +144,18 @@ SymbolInfo index::getSymbolInfo(const Decl *D) { Info.Lang = SymbolLanguage::ObjC; if (isUnitTest(cast(D))) Info.SubKinds |= (unsigned)SymbolSubKind::UnitTest; + if (D->hasAttr()) + Info.SubKinds |= (unsigned)SymbolSubKind::IBAnnotated; break; case Decl::ObjCProperty: Info.Kind = SymbolKind::InstanceProperty; Info.Lang = SymbolLanguage::ObjC; + checkForIBOutlets(D, Info.SubKinds); break; case Decl::ObjCIvar: Info.Kind = SymbolKind::Field; Info.Lang = SymbolLanguage::ObjC; + checkForIBOutlets(D, Info.SubKinds); break; case Decl::Namespace: Info.Kind = SymbolKind::Namespace; @@ -347,6 +360,8 @@ void index::applyForEachSymbolSubKind(SymbolSubKindSet SubKinds, APPLY_FOR_SUBKIND(TemplatePartialSpecialization); APPLY_FOR_SUBKIND(TemplateSpecialization); APPLY_FOR_SUBKIND(UnitTest); + APPLY_FOR_SUBKIND(IBAnnotated); + APPLY_FOR_SUBKIND(IBOutletCollection); #undef APPLY_FOR_SUBKIND } @@ -363,6 +378,8 @@ void index::printSymbolSubKinds(SymbolSubKindSet SubKinds, raw_ostream &OS) { case SymbolSubKind::TemplatePartialSpecialization: OS << "TPS"; break; case SymbolSubKind::TemplateSpecialization: OS << "TS"; break; case SymbolSubKind::UnitTest: OS << "test"; break; + case SymbolSubKind::IBAnnotated: OS << "IB"; break; + case SymbolSubKind::IBOutletCollection: OS << "IBColl"; break; } }); } diff --git a/test/Index/Core/index-subkinds.m b/test/Index/Core/index-subkinds.m index a78e1235a3a..e668d84ffa8 100644 --- a/test/Index/Core/index-subkinds.m +++ b/test/Index/Core/index-subkinds.m @@ -34,3 +34,15 @@ @implementation MyTestCase(cat) // CHECK: [[@LINE+1]]:1 | instance-method(test)/ObjC | testInCat | c:objc(cs)MyTestCase(im)testInCat | -[MyTestCase(cat) testInCat] | Def,Dyn,RelChild | rel: 1 - (void)testInCat {} @end + + +@class NSButton; +@interface IBCls +// CHECK: [[@LINE+2]]:34 | instance-method/ObjC | prop | c:objc(cs)IBCls(im)prop | -[IBCls prop] | Decl,Dyn,RelChild | rel: 1 +// CHECK: [[@LINE+1]]:34 | instance-property(IB)/ObjC | prop | c:objc(cs)IBCls(py)prop | | Decl,RelChild | rel: 1 +@property (readonly) IBOutlet id prop; +// CHECK: [[@LINE+1]]:54 | instance-property(IB,IBColl)/ObjC | propColl | c:objc(cs)IBCls(py)propColl | | Decl,RelChild | rel: 1 +@property (readonly) IBOutletCollection(NSButton) id propColl; +// CHECK: [[@LINE+1]]:1 | instance-method(IB)/ObjC | doIt | c:objc(cs)IBCls(im)doIt | -[IBCls doIt] | Decl,Dyn,RelChild | rel: 1 +-(IBAction)doIt; +@end From 843610a22ad0e34aba9b0595c09093f11c99936e Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Fri, 22 Apr 2016 07:21:04 +0000 Subject: [PATCH 534/742] [index] Change SymbolCXXTemplateKind to a 'SymbolSubKinds' bitset. This provides a more general and flexible way to annotate special symbols. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@267116 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Index/IndexSymbol.h | 18 +++++--- lib/Index/IndexSymbol.cpp | 60 ++++++++++++++++++-------- tools/c-index-test/core_main.cpp | 5 ++- tools/libclang/CXIndexDataConsumer.cpp | 19 ++++---- 4 files changed, 63 insertions(+), 39 deletions(-) diff --git a/include/clang/Index/IndexSymbol.h b/include/clang/Index/IndexSymbol.h index 56b12fa6bec..68d97d75ac2 100644 --- a/include/clang/Index/IndexSymbol.h +++ b/include/clang/Index/IndexSymbol.h @@ -59,12 +59,13 @@ enum class SymbolLanguage { CXX, }; -enum class SymbolCXXTemplateKind { - NonTemplate, - Template, - TemplatePartialSpecialization, - TemplateSpecialization, +enum class SymbolSubKind : uint8_t { + Generic = 1 << 0, + TemplatePartialSpecialization = 1 << 1, + TemplateSpecialization = 1 << 2, }; +static const unsigned SymbolSubKindBitNum = 3; +typedef unsigned SymbolSubKindSet; /// Set of roles that are attributed to symbol occurrences. enum class SymbolRole : uint16_t { @@ -99,7 +100,7 @@ struct SymbolRelation { struct SymbolInfo { SymbolKind Kind; - SymbolCXXTemplateKind TemplateKind; + SymbolSubKindSet SubKinds; SymbolLanguage Lang; }; @@ -113,9 +114,12 @@ void printSymbolRoles(SymbolRoleSet Roles, raw_ostream &OS); bool printSymbolName(const Decl *D, const LangOptions &LO, raw_ostream &OS); StringRef getSymbolKindString(SymbolKind K); -StringRef getTemplateKindStr(SymbolCXXTemplateKind TK); StringRef getSymbolLanguageString(SymbolLanguage K); +void applyForEachSymbolSubKind(SymbolSubKindSet SubKinds, + llvm::function_ref Fn); +void printSymbolSubKinds(SymbolSubKindSet SubKinds, raw_ostream &OS); + } // namespace index } // namespace clang diff --git a/lib/Index/IndexSymbol.cpp b/lib/Index/IndexSymbol.cpp index 9f91e6274d4..097aaf0721f 100644 --- a/lib/Index/IndexSymbol.cpp +++ b/lib/Index/IndexSymbol.cpp @@ -20,7 +20,7 @@ SymbolInfo index::getSymbolInfo(const Decl *D) { assert(D); SymbolInfo Info; Info.Kind = SymbolKind::Unknown; - Info.TemplateKind = SymbolCXXTemplateKind::NonTemplate; + Info.SubKinds = SymbolSubKindSet(); Info.Lang = SymbolLanguage::C; if (const TagDecl *TD = dyn_cast(D)) { @@ -46,9 +46,11 @@ SymbolInfo index::getSymbolInfo(const Decl *D) { Info.Lang = SymbolLanguage::CXX; if (isa(D)) { - Info.TemplateKind = SymbolCXXTemplateKind::TemplatePartialSpecialization; + Info.SubKinds |= (unsigned)SymbolSubKind::Generic; + Info.SubKinds |= (unsigned)SymbolSubKind::TemplatePartialSpecialization; } else if (isa(D)) { - Info.TemplateKind = SymbolCXXTemplateKind::TemplateSpecialization; + Info.SubKinds |= (unsigned)SymbolSubKind::Generic; + Info.SubKinds |= (unsigned)SymbolSubKind::TemplateSpecialization; } } else { @@ -141,12 +143,12 @@ SymbolInfo index::getSymbolInfo(const Decl *D) { } case Decl::ClassTemplate: Info.Kind = SymbolKind::Class; - Info.TemplateKind = SymbolCXXTemplateKind::Template; + Info.SubKinds |= (unsigned)SymbolSubKind::Generic; Info.Lang = SymbolLanguage::CXX; break; case Decl::FunctionTemplate: Info.Kind = SymbolKind::Function; - Info.TemplateKind = SymbolCXXTemplateKind::Template; + Info.SubKinds |= (unsigned)SymbolSubKind::Generic; Info.Lang = SymbolLanguage::CXX; if (const CXXMethodDecl *MD = dyn_cast_or_null( cast(D)->getTemplatedDecl())) { @@ -167,7 +169,7 @@ SymbolInfo index::getSymbolInfo(const Decl *D) { case Decl::TypeAliasTemplate: Info.Kind = SymbolKind::TypeAlias; Info.Lang = SymbolLanguage::CXX; - Info.TemplateKind = SymbolCXXTemplateKind::Template; + Info.SubKinds |= (unsigned)SymbolSubKind::Generic; break; case Decl::TypeAlias: Info.Kind = SymbolKind::TypeAlias; @@ -183,11 +185,13 @@ SymbolInfo index::getSymbolInfo(const Decl *D) { if (const FunctionDecl *FD = dyn_cast(D)) { if (FD->getTemplatedKind() == - FunctionDecl::TK_FunctionTemplateSpecialization) - Info.TemplateKind = SymbolCXXTemplateKind::TemplateSpecialization; + FunctionDecl::TK_FunctionTemplateSpecialization) { + Info.SubKinds |= (unsigned)SymbolSubKind::Generic; + Info.SubKinds |= (unsigned)SymbolSubKind::TemplateSpecialization; + } } - if (Info.TemplateKind != SymbolCXXTemplateKind::NonTemplate) + if (Info.SubKinds & (unsigned)SymbolSubKind::Generic) Info.Lang = SymbolLanguage::CXX; return Info; @@ -292,16 +296,6 @@ StringRef index::getSymbolKindString(SymbolKind K) { llvm_unreachable("invalid symbol kind"); } -StringRef index::getTemplateKindStr(SymbolCXXTemplateKind TK) { - switch (TK) { - case SymbolCXXTemplateKind::NonTemplate: return "NT"; - case SymbolCXXTemplateKind::Template : return "T"; - case SymbolCXXTemplateKind::TemplatePartialSpecialization : return "TPS"; - case SymbolCXXTemplateKind::TemplateSpecialization: return "TS"; - } - llvm_unreachable("invalid template kind"); -} - StringRef index::getSymbolLanguageString(SymbolLanguage K) { switch (K) { case SymbolLanguage::C: return "C"; @@ -310,3 +304,31 @@ StringRef index::getSymbolLanguageString(SymbolLanguage K) { } llvm_unreachable("invalid symbol language kind"); } + +void index::applyForEachSymbolSubKind(SymbolSubKindSet SubKinds, + llvm::function_ref Fn) { +#define APPLY_FOR_SUBKIND(K) \ + if (SubKinds & (unsigned)SymbolSubKind::K) \ + Fn(SymbolSubKind::K) + + APPLY_FOR_SUBKIND(Generic); + APPLY_FOR_SUBKIND(TemplatePartialSpecialization); + APPLY_FOR_SUBKIND(TemplateSpecialization); + +#undef APPLY_FOR_SUBKIND +} + +void index::printSymbolSubKinds(SymbolSubKindSet SubKinds, raw_ostream &OS) { + bool VisitedOnce = false; + applyForEachSymbolSubKind(SubKinds, [&](SymbolSubKind SubKind) { + if (VisitedOnce) + OS << ','; + else + VisitedOnce = true; + switch (SubKind) { + case SymbolSubKind::Generic: OS << "Gen"; break; + case SymbolSubKind::TemplatePartialSpecialization: OS << "TPS"; break; + case SymbolSubKind::TemplateSpecialization: OS << "TS"; break; + } + }); +} diff --git a/tools/c-index-test/core_main.cpp b/tools/c-index-test/core_main.cpp index 1881e31e207..b19c8cbba74 100644 --- a/tools/c-index-test/core_main.cpp +++ b/tools/c-index-test/core_main.cpp @@ -168,8 +168,9 @@ static bool printSourceSymbols(ArrayRef Args) { static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS) { OS << getSymbolKindString(SymInfo.Kind); - if (SymInfo.TemplateKind != SymbolCXXTemplateKind::NonTemplate) { - OS << '-' << getTemplateKindStr(SymInfo.TemplateKind); + if (SymInfo.SubKinds) { + OS << '-'; + printSymbolSubKinds(SymInfo.SubKinds, OS); } OS << '/' << getSymbolLanguageString(SymInfo.Lang); } diff --git a/tools/libclang/CXIndexDataConsumer.cpp b/tools/libclang/CXIndexDataConsumer.cpp index 3b556d441c7..59fa92bb21e 100644 --- a/tools/libclang/CXIndexDataConsumer.cpp +++ b/tools/libclang/CXIndexDataConsumer.cpp @@ -1134,7 +1134,7 @@ void CXIndexDataConsumer::translateLoc(SourceLocation Loc, static CXIdxEntityKind getEntityKindFromSymbolKind(SymbolKind K, SymbolLanguage L); static CXIdxEntityCXXTemplateKind -getEntityKindFromSymbolCXXTemplateKind(SymbolCXXTemplateKind K); +getEntityKindFromSymbolSubKinds(SymbolSubKindSet K); static CXIdxEntityLanguage getEntityLangFromSymbolLang(SymbolLanguage L); void CXIndexDataConsumer::getEntityInfo(const NamedDecl *D, @@ -1150,8 +1150,7 @@ void CXIndexDataConsumer::getEntityInfo(const NamedDecl *D, SymbolInfo SymInfo = getSymbolInfo(D); EntityInfo.kind = getEntityKindFromSymbolKind(SymInfo.Kind, SymInfo.Lang); - EntityInfo.templateKind = - getEntityKindFromSymbolCXXTemplateKind(SymInfo.TemplateKind); + EntityInfo.templateKind = getEntityKindFromSymbolSubKinds(SymInfo.SubKinds); EntityInfo.lang = getEntityLangFromSymbolLang(SymInfo.Lang); if (D->hasAttrs()) { @@ -1291,16 +1290,14 @@ static CXIdxEntityKind getEntityKindFromSymbolKind(SymbolKind K, SymbolLanguage } static CXIdxEntityCXXTemplateKind -getEntityKindFromSymbolCXXTemplateKind(SymbolCXXTemplateKind K) { - switch (K) { - case SymbolCXXTemplateKind::NonTemplate: return CXIdxEntity_NonTemplate; - case SymbolCXXTemplateKind::Template: return CXIdxEntity_Template; - case SymbolCXXTemplateKind::TemplatePartialSpecialization: +getEntityKindFromSymbolSubKinds(SymbolSubKindSet K) { + if (K & (unsigned)SymbolSubKind::TemplatePartialSpecialization) return CXIdxEntity_TemplatePartialSpecialization; - case SymbolCXXTemplateKind::TemplateSpecialization: + if (K & (unsigned)SymbolSubKind::TemplateSpecialization) return CXIdxEntity_TemplateSpecialization; - } - llvm_unreachable("invalid template kind"); + if (K & (unsigned)SymbolSubKind::Generic) + return CXIdxEntity_Template; + return CXIdxEntity_NonTemplate; } static CXIdxEntityLanguage getEntityLangFromSymbolLang(SymbolLanguage L) { From 4c46d12742e8a87d5643392ca75cddf7a6f78c71 Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Fri, 22 Apr 2016 07:21:10 +0000 Subject: [PATCH 535/742] [index] Add a SymbolSubKind for an ObjC unit test. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@267117 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Index/IndexSymbol.h | 3 ++- lib/Index/IndexSymbol.cpp | 36 ++++++++++++++++++++++++++++++- test/Index/Core/index-subkinds.m | 36 +++++++++++++++++++++++++++++++ tools/c-index-test/core_main.cpp | 3 ++- 4 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 test/Index/Core/index-subkinds.m diff --git a/include/clang/Index/IndexSymbol.h b/include/clang/Index/IndexSymbol.h index 68d97d75ac2..bd19771a188 100644 --- a/include/clang/Index/IndexSymbol.h +++ b/include/clang/Index/IndexSymbol.h @@ -63,8 +63,9 @@ enum class SymbolSubKind : uint8_t { Generic = 1 << 0, TemplatePartialSpecialization = 1 << 1, TemplateSpecialization = 1 << 2, + UnitTest = 1 << 3, }; -static const unsigned SymbolSubKindBitNum = 3; +static const unsigned SymbolSubKindBitNum = 4; typedef unsigned SymbolSubKindSet; /// Set of roles that are attributed to symbol occurrences. diff --git a/lib/Index/IndexSymbol.cpp b/lib/Index/IndexSymbol.cpp index 097aaf0721f..ba83d0cdef6 100644 --- a/lib/Index/IndexSymbol.cpp +++ b/lib/Index/IndexSymbol.cpp @@ -16,6 +16,30 @@ using namespace clang; using namespace clang::index; +/// \returns true if \c D is a subclass of 'XCTestCase'. +static bool isUnitTestCase(const ObjCInterfaceDecl *D) { + if (!D) + return false; + while (const ObjCInterfaceDecl *SuperD = D->getSuperClass()) { + if (SuperD->getName() == "XCTestCase") + return true; + D = SuperD; + } + return false; +} + +/// \returns true if \c D is in a subclass of 'XCTestCase', returns void, has +/// no parameters, and its name starts with 'test'. +static bool isUnitTest(const ObjCMethodDecl *D) { + if (!D->parameters().empty()) + return false; + if (!D->getReturnType()->isVoidType()) + return false; + if (!D->getSelector().getNameForSlot(0).startswith("test")) + return false; + return isUnitTestCase(D->getClassInterface()); +} + SymbolInfo index::getSymbolInfo(const Decl *D) { assert(D); SymbolInfo Info; @@ -84,10 +108,16 @@ SymbolInfo index::getSymbolInfo(const Decl *D) { case Decl::EnumConstant: Info.Kind = SymbolKind::EnumConstant; break; case Decl::ObjCInterface: - case Decl::ObjCImplementation: + case Decl::ObjCImplementation: { Info.Kind = SymbolKind::Class; Info.Lang = SymbolLanguage::ObjC; + const ObjCInterfaceDecl *ClsD = dyn_cast(D); + if (!ClsD) + ClsD = cast(D)->getClassInterface(); + if (isUnitTestCase(ClsD)) + Info.SubKinds |= (unsigned)SymbolSubKind::UnitTest; break; + } case Decl::ObjCProtocol: Info.Kind = SymbolKind::Protocol; Info.Lang = SymbolLanguage::ObjC; @@ -103,6 +133,8 @@ SymbolInfo index::getSymbolInfo(const Decl *D) { else Info.Kind = SymbolKind::ClassMethod; Info.Lang = SymbolLanguage::ObjC; + if (isUnitTest(cast(D))) + Info.SubKinds |= (unsigned)SymbolSubKind::UnitTest; break; case Decl::ObjCProperty: Info.Kind = SymbolKind::InstanceProperty; @@ -314,6 +346,7 @@ void index::applyForEachSymbolSubKind(SymbolSubKindSet SubKinds, APPLY_FOR_SUBKIND(Generic); APPLY_FOR_SUBKIND(TemplatePartialSpecialization); APPLY_FOR_SUBKIND(TemplateSpecialization); + APPLY_FOR_SUBKIND(UnitTest); #undef APPLY_FOR_SUBKIND } @@ -329,6 +362,7 @@ void index::printSymbolSubKinds(SymbolSubKindSet SubKinds, raw_ostream &OS) { case SymbolSubKind::Generic: OS << "Gen"; break; case SymbolSubKind::TemplatePartialSpecialization: OS << "TPS"; break; case SymbolSubKind::TemplateSpecialization: OS << "TS"; break; + case SymbolSubKind::UnitTest: OS << "test"; break; } }); } diff --git a/test/Index/Core/index-subkinds.m b/test/Index/Core/index-subkinds.m new file mode 100644 index 00000000000..a78e1235a3a --- /dev/null +++ b/test/Index/Core/index-subkinds.m @@ -0,0 +1,36 @@ +// RUN: c-index-test core -print-source-symbols -- %s -target x86_64-apple-macosx10.7 | FileCheck %s + +// CHECK: [[@LINE+1]]:12 | class/ObjC | XCTestCase | c:objc(cs)XCTestCase | _OBJC_CLASS_$_XCTestCase | Decl | rel: 0 +@interface XCTestCase +@end + +// CHECK: [[@LINE+1]]:12 | class(test)/ObjC | MyTestCase | c:objc(cs)MyTestCase | _OBJC_CLASS_$_MyTestCase | Decl | rel: 0 +@interface MyTestCase : XCTestCase +@end +// CHECK: [[@LINE+1]]:17 | class(test)/ObjC | MyTestCase | c:objc(cs)MyTestCase | | Def | rel: 0 +@implementation MyTestCase +// CHECK: [[@LINE+1]]:1 | instance-method(test)/ObjC | testMe | c:objc(cs)MyTestCase(im)testMe | -[MyTestCase testMe] | Def,Dyn,RelChild | rel: 1 +-(void)testMe {} +// CHECK: [[@LINE+1]]:1 | instance-method/ObjC | testResult | c:objc(cs)MyTestCase(im)testResult | -[MyTestCase testResult] | Def,Dyn,RelChild | rel: 1 +-(id)testResult { return 0; } +// CHECK: [[@LINE+1]]:1 | instance-method/ObjC | testWithInt: | c:objc(cs)MyTestCase(im)testWithInt: | -[MyTestCase testWithInt:] | Def,Dyn,RelChild | rel: 1 +-(void)testWithInt:(int)i {} +@end + +// CHECK: [[@LINE+1]]:12 | class(test)/ObjC | SubTestCase | c:objc(cs)SubTestCase | _OBJC_CLASS_$_SubTestCase | Decl | rel: 0 +@interface SubTestCase : MyTestCase +@end +// CHECK: [[@LINE+1]]:17 | class(test)/ObjC | SubTestCase | c:objc(cs)SubTestCase | | Def | rel: 0 +@implementation SubTestCase +// CHECK: [[@LINE+1]]:1 | instance-method(test)/ObjC | testIt2 | c:objc(cs)SubTestCase(im)testIt2 | -[SubTestCase testIt2] | Def,Dyn,RelChild | rel: 1 +-(void)testIt2 {} +@end + +// CHECK: [[@LINE+1]]:12 | extension/ObjC | cat | c:objc(cy)MyTestCase@cat | | Decl | rel: 0 +@interface MyTestCase(cat) +@end +// CHECK: [[@LINE+1]]:17 | extension/ObjC | MyTestCase | c:objc(cy)MyTestCase@cat | | Def | rel: 0 +@implementation MyTestCase(cat) +// CHECK: [[@LINE+1]]:1 | instance-method(test)/ObjC | testInCat | c:objc(cs)MyTestCase(im)testInCat | -[MyTestCase(cat) testInCat] | Def,Dyn,RelChild | rel: 1 +- (void)testInCat {} +@end diff --git a/tools/c-index-test/core_main.cpp b/tools/c-index-test/core_main.cpp index b19c8cbba74..d11b490e810 100644 --- a/tools/c-index-test/core_main.cpp +++ b/tools/c-index-test/core_main.cpp @@ -169,8 +169,9 @@ static bool printSourceSymbols(ArrayRef Args) { static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS) { OS << getSymbolKindString(SymInfo.Kind); if (SymInfo.SubKinds) { - OS << '-'; + OS << '('; printSymbolSubKinds(SymInfo.SubKinds, OS); + OS << ')'; } OS << '/' << getSymbolLanguageString(SymInfo.Lang); } From 012f40adae0adba2c92198b65907ae685cf080df Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Fri, 22 Apr 2016 07:21:16 +0000 Subject: [PATCH 536/742] [index] Add SymbolSubKinds for ObjC IB annotations. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@267118 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Index/IndexSymbol.h | 4 +++- lib/Index/IndexSymbol.cpp | 17 +++++++++++++++++ test/Index/Core/index-subkinds.m | 12 ++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/include/clang/Index/IndexSymbol.h b/include/clang/Index/IndexSymbol.h index bd19771a188..b0bc93e464b 100644 --- a/include/clang/Index/IndexSymbol.h +++ b/include/clang/Index/IndexSymbol.h @@ -64,8 +64,10 @@ enum class SymbolSubKind : uint8_t { TemplatePartialSpecialization = 1 << 1, TemplateSpecialization = 1 << 2, UnitTest = 1 << 3, + IBAnnotated = 1 << 4, + IBOutletCollection = 1 << 5, }; -static const unsigned SymbolSubKindBitNum = 4; +static const unsigned SymbolSubKindBitNum = 6; typedef unsigned SymbolSubKindSet; /// Set of roles that are attributed to symbol occurrences. diff --git a/lib/Index/IndexSymbol.cpp b/lib/Index/IndexSymbol.cpp index ba83d0cdef6..13a84523007 100644 --- a/lib/Index/IndexSymbol.cpp +++ b/lib/Index/IndexSymbol.cpp @@ -40,6 +40,15 @@ static bool isUnitTest(const ObjCMethodDecl *D) { return isUnitTestCase(D->getClassInterface()); } +static void checkForIBOutlets(const Decl *D, SymbolSubKindSet &SubKindSet) { + if (D->hasAttr()) { + SubKindSet |= (unsigned)SymbolSubKind::IBAnnotated; + } else if (D->hasAttr()) { + SubKindSet |= (unsigned)SymbolSubKind::IBAnnotated; + SubKindSet |= (unsigned)SymbolSubKind::IBOutletCollection; + } +} + SymbolInfo index::getSymbolInfo(const Decl *D) { assert(D); SymbolInfo Info; @@ -135,14 +144,18 @@ SymbolInfo index::getSymbolInfo(const Decl *D) { Info.Lang = SymbolLanguage::ObjC; if (isUnitTest(cast(D))) Info.SubKinds |= (unsigned)SymbolSubKind::UnitTest; + if (D->hasAttr()) + Info.SubKinds |= (unsigned)SymbolSubKind::IBAnnotated; break; case Decl::ObjCProperty: Info.Kind = SymbolKind::InstanceProperty; Info.Lang = SymbolLanguage::ObjC; + checkForIBOutlets(D, Info.SubKinds); break; case Decl::ObjCIvar: Info.Kind = SymbolKind::Field; Info.Lang = SymbolLanguage::ObjC; + checkForIBOutlets(D, Info.SubKinds); break; case Decl::Namespace: Info.Kind = SymbolKind::Namespace; @@ -347,6 +360,8 @@ void index::applyForEachSymbolSubKind(SymbolSubKindSet SubKinds, APPLY_FOR_SUBKIND(TemplatePartialSpecialization); APPLY_FOR_SUBKIND(TemplateSpecialization); APPLY_FOR_SUBKIND(UnitTest); + APPLY_FOR_SUBKIND(IBAnnotated); + APPLY_FOR_SUBKIND(IBOutletCollection); #undef APPLY_FOR_SUBKIND } @@ -363,6 +378,8 @@ void index::printSymbolSubKinds(SymbolSubKindSet SubKinds, raw_ostream &OS) { case SymbolSubKind::TemplatePartialSpecialization: OS << "TPS"; break; case SymbolSubKind::TemplateSpecialization: OS << "TS"; break; case SymbolSubKind::UnitTest: OS << "test"; break; + case SymbolSubKind::IBAnnotated: OS << "IB"; break; + case SymbolSubKind::IBOutletCollection: OS << "IBColl"; break; } }); } diff --git a/test/Index/Core/index-subkinds.m b/test/Index/Core/index-subkinds.m index a78e1235a3a..e668d84ffa8 100644 --- a/test/Index/Core/index-subkinds.m +++ b/test/Index/Core/index-subkinds.m @@ -34,3 +34,15 @@ @implementation MyTestCase(cat) // CHECK: [[@LINE+1]]:1 | instance-method(test)/ObjC | testInCat | c:objc(cs)MyTestCase(im)testInCat | -[MyTestCase(cat) testInCat] | Def,Dyn,RelChild | rel: 1 - (void)testInCat {} @end + + +@class NSButton; +@interface IBCls +// CHECK: [[@LINE+2]]:34 | instance-method/ObjC | prop | c:objc(cs)IBCls(im)prop | -[IBCls prop] | Decl,Dyn,RelChild | rel: 1 +// CHECK: [[@LINE+1]]:34 | instance-property(IB)/ObjC | prop | c:objc(cs)IBCls(py)prop | | Decl,RelChild | rel: 1 +@property (readonly) IBOutlet id prop; +// CHECK: [[@LINE+1]]:54 | instance-property(IB,IBColl)/ObjC | propColl | c:objc(cs)IBCls(py)propColl | | Decl,RelChild | rel: 1 +@property (readonly) IBOutletCollection(NSButton) id propColl; +// CHECK: [[@LINE+1]]:1 | instance-method(IB)/ObjC | doIt | c:objc(cs)IBCls(im)doIt | -[IBCls doIt] | Decl,Dyn,RelChild | rel: 1 +-(IBAction)doIt; +@end From 574e623db0be66e434f6b4e854f0f564288da3f0 Mon Sep 17 00:00:00 2001 From: Betul Buyukkurt Date: Sat, 23 Jan 2016 22:50:44 +0000 Subject: [PATCH 537/742] Clang changes for value profiling Differential Revision: http://reviews.llvm.org/D8940 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@258650 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 7ff19db5823c50764fa377576e64f973c94f47f4) --- lib/CodeGen/CGCall.cpp | 5 ++ lib/CodeGen/CodeGenPGO.cpp | 85 ++++++++++++++++++++++++++++++++-- lib/CodeGen/CodeGenPGO.h | 10 +++- test/Profile/c-indirect-call.c | 15 ++++++ 4 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 test/Profile/c-indirect-call.c diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index ef32a9363ba..827171c5001 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -3918,6 +3918,11 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, CS.setAttributes(Attrs); CS.setCallingConv(static_cast(CallingConv)); + // Insert instrumentation or attach profile metadata at indirect call sites + if (!CS.getCalledFunction()) + PGO.valueProfile(Builder, llvm::IPVK_IndirectCallTarget, + CS.getInstruction(), Callee); + // In ObjC ARC mode with no ObjC ARC exception safety, tell the ARC // optimizer it can aggressively ignore unwind edges. if (CGM.getLangOpts().ObjCAutoRefCount) diff --git a/lib/CodeGen/CodeGenPGO.cpp b/lib/CodeGen/CodeGenPGO.cpp index 2c0d93b394a..df043537dd4 100644 --- a/lib/CodeGen/CodeGenPGO.cpp +++ b/lib/CodeGen/CodeGenPGO.cpp @@ -18,11 +18,14 @@ #include "clang/AST/StmtVisitor.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/MDBuilder.h" -#include "llvm/ProfileData/InstrProfReader.h" #include "llvm/Support/Endian.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MD5.h" +static llvm::cl::opt EnableValueProfiling( + "enable-value-profiling", llvm::cl::ZeroOrMore, + llvm::cl::desc("Enable value profiling"), llvm::cl::init(false)); + using namespace clang; using namespace CodeGen; @@ -740,12 +743,83 @@ void CodeGenPGO::emitCounterIncrement(CGBuilderTy &Builder, const Stmt *S) { Builder.getInt32(Counter)}); } +// This method either inserts a call to the profile run-time during +// instrumentation or puts profile data into metadata for PGO use. +void CodeGenPGO::valueProfile(CGBuilderTy &Builder, uint32_t ValueKind, + llvm::Instruction *ValueSite, llvm::Value *ValuePtr) { + + if (!EnableValueProfiling) + return; + + if (!ValuePtr || !ValueSite || !Builder.GetInsertBlock()) + return; + + bool InstrumentValueSites = CGM.getCodeGenOpts().ProfileInstrGenerate; + if (InstrumentValueSites && RegionCounterMap) { + llvm::LLVMContext &Ctx = CGM.getLLVMContext(); + auto *I8PtrTy = llvm::Type::getInt8PtrTy(Ctx); + llvm::Value *Args[5] = { + llvm::ConstantExpr::getBitCast(FuncNameVar, I8PtrTy), + Builder.getInt64(FunctionHash), + Builder.CreatePtrToInt(ValuePtr, Builder.getInt64Ty()), + Builder.getInt32(ValueKind), + Builder.getInt32(NumValueSites[ValueKind]++) + }; + Builder.CreateCall( + CGM.getIntrinsic(llvm::Intrinsic::instrprof_value_profile), Args); + return; + } + + llvm::IndexedInstrProfReader *PGOReader = CGM.getPGOReader(); + if (PGOReader && haveRegionCounts()) { + // We record the top most called three functions at each call site. + // Profile metadata contains "VP" string identifying this metadata + // as value profiling data, then a uint32_t value for the value profiling + // kind, a uint64_t value for the total number of times the call is + // executed, followed by the function hash and execution count (uint64_t) + // pairs for each function. + if (NumValueSites[ValueKind] >= ProfRecord->getNumValueSites(ValueKind)) + return; + uint32_t NV = ProfRecord->getNumValueDataForSite(ValueKind, + NumValueSites[ValueKind]); + std::unique_ptr VD = + ProfRecord->getValueForSite(ValueKind, NumValueSites[ValueKind]); + + uint64_t Sum = 0; + for (uint32_t I = 0; I < NV; ++I) + Sum += VD[I].Count; + + llvm::LLVMContext &Ctx = CGM.getLLVMContext(); + llvm::MDBuilder MDHelper(Ctx); + SmallVector Vals; + Vals.push_back(MDHelper.createString("VP")); + Vals.push_back(MDHelper.createConstant( + llvm::ConstantInt::get(llvm::Type::getInt32Ty(Ctx), ValueKind))); + Vals.push_back(MDHelper.createConstant( + llvm::ConstantInt::get(llvm::Type::getInt64Ty(Ctx), Sum))); + + uint32_t MDCount = 3; + for (uint32_t I = 0; I < NV; ++I) { + Vals.push_back(MDHelper.createConstant( + llvm::ConstantInt::get(llvm::Type::getInt64Ty(Ctx), VD[I].Value))); + Vals.push_back(MDHelper.createConstant( + llvm::ConstantInt::get(llvm::Type::getInt64Ty(Ctx), VD[I].Count))); + if (--MDCount == 0) + break; + } + ValueSite->setMetadata( + llvm::LLVMContext::MD_prof, llvm::MDNode::get(Ctx, Vals)); + NumValueSites[ValueKind]++; + } +} + void CodeGenPGO::loadRegionCounts(llvm::IndexedInstrProfReader *PGOReader, bool IsInMainFile) { CGM.getPGOStats().addVisited(IsInMainFile); RegionCounts.clear(); - if (std::error_code EC = - PGOReader->getFunctionCounts(FuncName, FunctionHash, RegionCounts)) { + llvm::ErrorOr RecordErrorOr = + PGOReader->getInstrProfRecord(FuncName, FunctionHash); + if (std::error_code EC = RecordErrorOr.getError()) { if (EC == llvm::instrprof_error::unknown_function) CGM.getPGOStats().addMissing(IsInMainFile); else if (EC == llvm::instrprof_error::hash_mismatch) @@ -753,8 +827,11 @@ void CodeGenPGO::loadRegionCounts(llvm::IndexedInstrProfReader *PGOReader, else if (EC == llvm::instrprof_error::malformed) // TODO: Consider a more specific warning for this case. CGM.getPGOStats().addMismatched(IsInMainFile); - RegionCounts.clear(); + return; } + ProfRecord = + llvm::make_unique(std::move(RecordErrorOr.get())); + RegionCounts = ProfRecord->Counts; } /// \brief Calculate what to divide by to scale weights. diff --git a/lib/CodeGen/CodeGenPGO.h b/lib/CodeGen/CodeGenPGO.h index 6bf29ecaa7c..a181cb958c7 100644 --- a/lib/CodeGen/CodeGenPGO.h +++ b/lib/CodeGen/CodeGenPGO.h @@ -19,6 +19,7 @@ #include "CodeGenTypes.h" #include "clang/Frontend/CodeGenOptions.h" #include "llvm/ADT/StringMap.h" +#include "llvm/ProfileData/InstrProfReader.h" #include "llvm/Support/MemoryBuffer.h" #include @@ -32,10 +33,12 @@ class CodeGenPGO { std::string FuncName; llvm::GlobalVariable *FuncNameVar; + unsigned NumValueSites[llvm::IPVK_Last + 1]; unsigned NumRegionCounters; uint64_t FunctionHash; std::unique_ptr> RegionCounterMap; std::unique_ptr> StmtCountMap; + std::unique_ptr ProfRecord; std::vector RegionCounts; uint64_t CurrentRegionCount; /// \brief A flag that is set to true when this function doesn't need @@ -44,8 +47,8 @@ class CodeGenPGO { public: CodeGenPGO(CodeGenModule &CGM) - : CGM(CGM), NumRegionCounters(0), FunctionHash(0), CurrentRegionCount(0), - SkipCoverageMapping(false) {} + : CGM(CGM), NumValueSites{0}, NumRegionCounters(0), + FunctionHash(0), CurrentRegionCount(0), SkipCoverageMapping(false) {} /// Whether or not we have PGO region data for the current function. This is /// false both when we have no data at all and when our data has been @@ -87,6 +90,9 @@ class CodeGenPGO { /// for an unused declaration. void emitEmptyCounterMapping(const Decl *D, StringRef FuncName, llvm::GlobalValue::LinkageTypes Linkage); + // Insert instrumentation or attach profile metadata at value sites + void valueProfile(CGBuilderTy &Builder, uint32_t ValueKind, + llvm::Instruction *ValueSite, llvm::Value *ValuePtr); private: void setFuncName(llvm::Function *Fn); void setFuncName(StringRef Name, llvm::GlobalValue::LinkageTypes Linkage); diff --git a/test/Profile/c-indirect-call.c b/test/Profile/c-indirect-call.c new file mode 100644 index 00000000000..0d8f3babdbd --- /dev/null +++ b/test/Profile/c-indirect-call.c @@ -0,0 +1,15 @@ +// Check the data structures emitted by instrumentation. +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.9 -main-file-name c-indirect-call.c %s -o - -emit-llvm -fprofile-instr-generate -mllvm -enable-value-profiling | FileCheck %s + +void (*foo)(void); + +int main(void) { +// CHECK: [[REG1:%[0-9]+]] = load void ()*, void ()** @foo, align 8 +// CHECK-NEXT: call void [[REG1]]() +// CHECK-NEXT: [[REG2:%[0-9]+]] = ptrtoint void ()* [[REG1]] to i64 +// CHECK-NEXT: call void @__llvm_profile_instrument_target(i64 [[REG2]], i8* bitcast ({ i32, i32, i64, i8*, i64*, i8*, i8*, [1 x i16] }* @__profd_main to i8*), i32 0) + foo(); + return 0; +} + +// CHECK: declare void @__llvm_profile_instrument_target(i64, i8*, i32) From 6de996f72e698f92b1a9dac402678a52289dc2d7 Mon Sep 17 00:00:00 2001 From: Xinliang David Li Date: Thu, 28 Jan 2016 18:25:53 +0000 Subject: [PATCH 538/742] [PGO] test case cleanups 1. Make test case more focused and robust by focusing on what to be tested (linkage, icall) -- make it easier to validate 2. Testing linkages of data and counter variables instead of names. Counters and data are more relavant to be tested. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@259067 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit c599c9b4378f4d99500959895159e4dd26980ee6) --- test/Profile/c-indirect-call.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Profile/c-indirect-call.c b/test/Profile/c-indirect-call.c index 0d8f3babdbd..d73d09a9a16 100644 --- a/test/Profile/c-indirect-call.c +++ b/test/Profile/c-indirect-call.c @@ -7,7 +7,7 @@ int main(void) { // CHECK: [[REG1:%[0-9]+]] = load void ()*, void ()** @foo, align 8 // CHECK-NEXT: call void [[REG1]]() // CHECK-NEXT: [[REG2:%[0-9]+]] = ptrtoint void ()* [[REG1]] to i64 -// CHECK-NEXT: call void @__llvm_profile_instrument_target(i64 [[REG2]], i8* bitcast ({ i32, i32, i64, i8*, i64*, i8*, i8*, [1 x i16] }* @__profd_main to i8*), i32 0) +// CHECK-NEXT: call void @__llvm_profile_instrument_target(i64 [[REG2]], i8* bitcast ({{.*}}* @__profd_main to i8*), i32 0) foo(); return 0; } From 58f5ae99669d876a99ea9576b0b0c5bd09bfee2c Mon Sep 17 00:00:00 2001 From: Rong Xu Date: Fri, 22 Apr 2016 21:19:05 +0000 Subject: [PATCH 539/742] PGO] PGOFuncName meta data if PGOFuncName is different from function's raw name Write out the PGOFuncName meta data if PGOFuncName is different from function's raw name. This should only apply to internal linkage functions. This is to be consumed by indirect-call promotion when called in LTO optimization pass. Differential Revision: http://reviews.llvm.org/D18624 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@267224 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 83402ed45b681e9f38ce6626e5899c19159ceb29) --- lib/CodeGen/CodeGenPGO.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/CodeGen/CodeGenPGO.cpp b/lib/CodeGen/CodeGenPGO.cpp index df043537dd4..23e9323c48e 100644 --- a/lib/CodeGen/CodeGenPGO.cpp +++ b/lib/CodeGen/CodeGenPGO.cpp @@ -43,6 +43,8 @@ void CodeGenPGO::setFuncName(StringRef Name, void CodeGenPGO::setFuncName(llvm::Function *Fn) { setFuncName(Fn->getName(), Fn->getLinkage()); + // Create PGOFuncName meta data. + llvm::createPGOFuncNameMetadata(*Fn, FuncName); } namespace { From ad5b3a61e49a1793d492914c05d16cb652583f3e Mon Sep 17 00:00:00 2001 From: Adam Nemet Date: Tue, 19 Apr 2016 22:17:45 +0000 Subject: [PATCH 540/742] [Parse] Use StringSwitch to improve readability. NFC A subsequent patch will propose a "distribute" loop hint. Similarly to unroll, this does not have a "assume_safety" argument either so this condition will get more complex. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@266827 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 01c891b6762ad2a6eb80770cf108ad42f098139b) --- lib/Parse/ParsePragma.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/Parse/ParsePragma.cpp b/lib/Parse/ParsePragma.cpp index bc70942851e..87916544995 100644 --- a/lib/Parse/ParsePragma.cpp +++ b/lib/Parse/ParsePragma.cpp @@ -841,10 +841,14 @@ bool Parser::HandlePragmaLoopHint(LoopHint &Hint) { ConsumeToken(); // The annotation token. SourceLocation StateLoc = Toks[0].getLocation(); IdentifierInfo *StateInfo = Toks[0].getIdentifierInfo(); - if (!StateInfo || - (!StateInfo->isStr("enable") && !StateInfo->isStr("disable") && - ((OptionUnroll && !StateInfo->isStr("full")) || - (!OptionUnroll && !StateInfo->isStr("assume_safety"))))) { + + bool Valid = StateInfo && + llvm::StringSwitch(StateInfo->getName()) + .Cases("enable", "disable", true) + .Case("full", OptionUnroll) + .Case("assume_safety", !OptionUnroll) + .Default(false); + if (!Valid) { Diag(Toks[0].getLocation(), diag::err_pragma_invalid_keyword) << /*FullKeyword=*/OptionUnroll; return false; From 1f1060fa0a0fb4b2160e1a1c3d86a25e1d45d2da Mon Sep 17 00:00:00 2001 From: Adam Nemet Date: Tue, 19 Apr 2016 22:29:24 +0000 Subject: [PATCH 541/742] [Parse] Reuse OptionUnroll rather than matching it again. NFC git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@266829 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 9625d162e52552164d54e95ab4800f7223516570) --- lib/Parse/ParsePragma.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/Parse/ParsePragma.cpp b/lib/Parse/ParsePragma.cpp index 87916544995..5b4f935d2c8 100644 --- a/lib/Parse/ParsePragma.cpp +++ b/lib/Parse/ParsePragma.cpp @@ -824,8 +824,7 @@ bool Parser::HandlePragmaLoopHint(LoopHint &Hint) { StateOption = llvm::StringSwitch(OptionInfo->getName()) .Case("vectorize", true) .Case("interleave", true) - .Case("unroll", true) - .Default(false); + .Default(false) || OptionUnroll; } // Verify loop hint has an argument. From 12b94271233fba4754bed98cc040078086dd3839 Mon Sep 17 00:00:00 2001 From: Peter Zotov Date: Wed, 9 Mar 2016 05:18:16 +0000 Subject: [PATCH 542/742] Accept absolute paths in the -fuse-ld option. This patch extends the -fuse-ld option to accept a full path to an executable and use it verbatim to invoke the linker. There are generally two reasons to desire this. The first reason relates to the sad truth is that Clang is retargetable, Binutils are not. While any Clang from a binary distribution is sufficient to compile code for a wide range of architectures and prefixed BFD linkers (e.g. installed as /usr/bin/arm-none-linux-gnueabi-ld) as well as cross-compiled libc's (for non-bare-metal targets) are widely available, including on all Debian derivatives, it is impossible to use them together because the -fuse-ld= option allows to specify neither a linker prefix nor a full path to one. The second reason is linker development, both when porting existing linkers to new architectures and when working on a new linker such as LLD. Differential Revision: http://reviews.llvm.org/D17952 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@262996 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Driver/ToolChain.cpp | 33 ++++++++++++++++++++------------- test/Driver/fuse-ld.c | 6 ++++++ 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/lib/Driver/ToolChain.cpp b/lib/Driver/ToolChain.cpp index cbbd485a9b7..53bf63e775d 100644 --- a/lib/Driver/ToolChain.cpp +++ b/lib/Driver/ToolChain.cpp @@ -341,19 +341,26 @@ std::string ToolChain::GetProgramPath(const char *Name) const { std::string ToolChain::GetLinkerPath() const { if (Arg *A = Args.getLastArg(options::OPT_fuse_ld_EQ)) { - StringRef Suffix = A->getValue(); - - // If we're passed -fuse-ld= with no argument, or with the argument ld, - // then use whatever the default system linker is. - if (Suffix.empty() || Suffix == "ld") - return GetProgramPath("ld"); - - llvm::SmallString<8> LinkerName("ld."); - LinkerName.append(Suffix); - - std::string LinkerPath(GetProgramPath(LinkerName.c_str())); - if (llvm::sys::fs::exists(LinkerPath)) - return LinkerPath; + StringRef UseLinker = A->getValue(); + + if (llvm::sys::path::is_absolute(UseLinker)) { + // If we're passed -fuse-ld= with what looks like an absolute path, + // don't attempt to second-guess that. + if (llvm::sys::fs::exists(UseLinker)) + return UseLinker; + } else { + // If we're passed -fuse-ld= with no argument, or with the argument ld, + // then use whatever the default system linker is. + if (UseLinker.empty() || UseLinker == "ld") + return GetProgramPath("ld"); + + llvm::SmallString<8> LinkerName("ld."); + LinkerName.append(UseLinker); + + std::string LinkerPath(GetProgramPath(LinkerName.c_str())); + if (llvm::sys::fs::exists(LinkerPath)) + return LinkerPath; + } getDriver().Diag(diag::err_drv_invalid_linker_name) << A->getAsString(Args); return ""; diff --git a/test/Driver/fuse-ld.c b/test/Driver/fuse-ld.c index bd25b8deb32..ca89eb99716 100644 --- a/test/Driver/fuse-ld.c +++ b/test/Driver/fuse-ld.c @@ -1,3 +1,9 @@ +// RUN: %clang %s -### \ +// RUN: -fuse-ld=/usr/local/bin/or1k-linux-ld 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-ABSOLUTE-LD +// CHECK-ABSOLUTE-LD: /usr/local/bin/or1k-linux-ld + + // RUN: %clang %s -### \ // RUN: -target x86_64-unknown-freebsd 2>&1 \ // RUN: | FileCheck %s --check-prefix=CHECK-FREEBSD-LD From cc665b519673de4a0411246a28d6474445f0c6ef Mon Sep 17 00:00:00 2001 From: Mehdi Amini Date: Sun, 24 Apr 2016 03:44:55 +0000 Subject: [PATCH 543/742] Make thinlto clang test more robust against LLVM changes. We should just test the effect of the clang level option here, i.e. that a summary is correctly emitted with -flto=thin From: Mehdi Amini git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@267321 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/Misc/thinlto.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/test/Misc/thinlto.c b/test/Misc/thinlto.c index ae2c148890d..ec0f03f5a2a 100644 --- a/test/Misc/thinlto.c +++ b/test/Misc/thinlto.c @@ -1,9 +1,4 @@ // RUN: %clang_cc1 -flto=thin -emit-llvm-bc < %s | llvm-bcanalyzer -dump | FileCheck %s +// ; Check that the -flto=thin option emits a summary // CHECK: Date: Sun, 24 Apr 2016 22:22:29 +0000 Subject: [PATCH 544/742] Debug info: Apply an empty debug location for global OpenMP destructors. LLVM really wants a debug location on every inlinable call in a function with debug info, because it otherwise cannot set up inlining debug info. This change applies an artificial line 0 debug location (which is how DWARF marks automatically generated code that has no corresponding source code) to the .__kmpc_global_dtor_. functions to avoid the LLVM Verifier complaining. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@267369 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit c9192fab899a2b4d5b8ae436c9910c4ef6708120) --- lib/CodeGen/CGOpenMPRuntime.cpp | 3 +++ test/OpenMP/threadprivate_codegen.cpp | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/CodeGen/CGOpenMPRuntime.cpp b/lib/CodeGen/CGOpenMPRuntime.cpp index fbf4d351ac3..6c796c70b04 100644 --- a/lib/CodeGen/CGOpenMPRuntime.cpp +++ b/lib/CodeGen/CGOpenMPRuntime.cpp @@ -1181,8 +1181,11 @@ llvm::Function *CGOpenMPRuntime::emitThreadPrivateVarDefinition( auto FTy = CGM.getTypes().GetFunctionType(FI); auto Fn = CGM.CreateGlobalInitOrDestructFunction( FTy, ".__kmpc_global_dtor_.", FI, Loc); + auto NL = ApplyDebugLocation::CreateEmpty(DtorCGF); DtorCGF.StartFunction(GlobalDecl(), CGM.getContext().VoidTy, Fn, FI, Args, SourceLocation()); + // Create a scope with an artificial location for the body of this function. + auto AL = ApplyDebugLocation::CreateArtificial(DtorCGF); auto ArgVal = DtorCGF.EmitLoadOfScalar( DtorCGF.GetAddrOfLocalVar(&Dst), /*Volatile=*/false, CGM.getContext().VoidPtrTy, Dst.getLocation()); diff --git a/test/OpenMP/threadprivate_codegen.cpp b/test/OpenMP/threadprivate_codegen.cpp index 793f6c58087..006d6a57dda 100644 --- a/test/OpenMP/threadprivate_codegen.cpp +++ b/test/OpenMP/threadprivate_codegen.cpp @@ -221,7 +221,7 @@ static S1 gs1(5); // CHECK-DEBUG: store i8* %0, i8** [[ARG_ADDR:%.*]], // CHECK-DEBUG: [[ARG:%.+]] = load i8*, i8** [[ARG_ADDR]] // CHECK-DEBUG: [[RES:%.*]] = bitcast i8* [[ARG]] to [[S1]]* -// CHECK-DEBUG-NEXT: call {{.*}} [[S1_CTOR:@.+]]([[S1]]* [[RES]], {{.*}} 5) +// CHECK-DEBUG-NEXT: call {{.*}} [[S1_CTOR:@.+]]([[S1]]* [[RES]], {{.*}} 5){{.*}}, !dbg // CHECK-DEBUG: [[ARG:%.+]] = load i8*, i8** [[ARG_ADDR]] // CHECK-DEBUG: ret i8* [[ARG]] // CHECK-DEBUG-NEXT: } @@ -230,7 +230,7 @@ static S1 gs1(5); // CHECK-DEBUG: store i8* %0, i8** [[ARG_ADDR:%.*]], // CHECK-DEBUG: [[ARG:%.+]] = load i8*, i8** [[ARG_ADDR]] // CHECK-DEBUG: [[RES:%.*]] = bitcast i8* [[ARG]] to [[S1]]* -// CHECK-DEBUG-NEXT: call {{.*}} [[S1_DTOR:@.+]]([[S1]]* [[RES]]) +// CHECK-DEBUG-NEXT: call {{.*}} [[S1_DTOR:@.+]]([[S1]]* [[RES]]){{.*}}, !dbg // CHECK-DEBUG-NEXT: ret void // CHECK-DEBUG-NEXT: } // CHECK-DEBUG: define {{.*}} [[S1_DTOR]]([[S1]]* {{.*}}) From 865c95288d24acb2112af061c438c44867b6963a Mon Sep 17 00:00:00 2001 From: Chris Bieneman Date: Tue, 15 Mar 2016 18:08:20 +0000 Subject: [PATCH 545/742] [Driver] [Darwin] Fix linking libclang_rt.profile_*sim.a Summary: isTarget*() calls are order-dependent. This is because iOS Sim *is* iOS. This means checks for the simulator version of the platform must always be ahead of checks for the embedded platform. Reviewers: zaks.anna, bogner Subscribers: cfe-commits Differential Revision: http://reviews.llvm.org/D17947 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@263567 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit f305de2b46ef58cfee26ad0c5d908a3a3b24675e) --- lib/Driver/ToolChains.cpp | 30 ++---------------------------- test/Driver/darwin-ld.c | 6 ++++++ 2 files changed, 8 insertions(+), 28 deletions(-) diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 001da47ab4b..e334754f16d 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -323,34 +323,8 @@ void Darwin::addProfileRTLibs(const ArgList &Args, ArgStringList &CmdArgs) const { if (!needsProfileRT(Args)) return; - // TODO: Clean this up once autoconf is gone - SmallString<128> P(getDriver().ResourceDir); - llvm::sys::path::append(P, "lib", "darwin"); - const char *Library = "libclang_rt.profile_osx.a"; - - // Select the appropriate runtime library for the target. - if (isTargetWatchOS()) { - Library = "libclang_rt.profile_watchos.a"; - } else if (isTargetWatchOSSimulator()) { - llvm::sys::path::append(P, "libclang_rt.profile_watchossim.a"); - Library = getVFS().exists(P) ? "libclang_rt.profile_watchossim.a" - : "libclang_rt.profile_watchos.a"; - } else if (isTargetTvOS()) { - Library = "libclang_rt.profile_tvos.a"; - } else if (isTargetTvOSSimulator()) { - llvm::sys::path::append(P, "libclang_rt.profile_tvossim.a"); - Library = getVFS().exists(P) ? "libclang_rt.profile_tvossim.a" - : "libclang_rt.profile_tvos.a"; - } else if (isTargetIPhoneOS()) { - Library = "libclang_rt.profile_ios.a"; - } else if (isTargetIOSSimulator()) { - llvm::sys::path::append(P, "libclang_rt.profile_iossim.a"); - Library = getVFS().exists(P) ? "libclang_rt.profile_iossim.a" - : "libclang_rt.profile_ios.a"; - } else { - assert(isTargetMacOS() && "unexpected non MacOS platform"); - } - AddLinkRuntimeLib(Args, CmdArgs, Library, + AddLinkRuntimeLib(Args, CmdArgs, (Twine("libclang_rt.profile_") + + getOSLibraryNameSuffix() + ".a").str(), /*AlwaysLink*/ true); return; } diff --git a/test/Driver/darwin-ld.c b/test/Driver/darwin-ld.c index 66c5206b4c8..3aceda5b512 100644 --- a/test/Driver/darwin-ld.c +++ b/test/Driver/darwin-ld.c @@ -152,6 +152,12 @@ // RUN: FileCheck -check-prefix=LINK_NO_IOS_ARM64_CRT1 %s < %t.log // LINK_NO_IOS_ARM64_CRT1-NOT: crt +// RUN: %clang -target x86_64-apple-ios6.0 -miphoneos-version-min=6.0 -fprofile-instr-generate -### %t.o 2> %t.log +// RUN: FileCheck -check-prefix=LINK_IOSSIM_PROFILE %s < %t.log +// LINK_IOSSIM_PROFILE: {{ld(.exe)?"}} +// LINK_IOSSIM_PROFILE: libclang_rt.profile_iossim.a +// LINK_IOSSIM_PROFILE: libclang_rt.ios.a + // RUN: %clang -target arm64-apple-tvos8.3 -mtvos-version-min=8.3 -### %t.o 2> %t.log // RUN: FileCheck -check-prefix=LINK_TVOS_ARM64 %s < %t.log // LINK_TVOS_ARM64: {{ld(.exe)?"}} From 013870a10697c15fe86b569644ec23675d48e2b9 Mon Sep 17 00:00:00 2001 From: Richard Trieu Date: Tue, 5 Apr 2016 21:13:54 +0000 Subject: [PATCH 546/742] Fix a crash on invalid with template handling This is a fix for https://llvm.org/bugs/show_bug.cgi?id=25561 which was a crash on invalid. Change the handling of invalid decls to have a catch-all case to prevent unexpecting decls from triggering an assertion. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@265467 91177308-0d34-0410-b5e6-96231b3b80d8 (cherry picked from commit 499ed1eba714d86e29985c587a3bbd6b037c5d78) --- include/clang/Basic/DiagnosticSemaKinds.td | 2 +- lib/Sema/SemaTemplate.cpp | 18 ++++++++++++------ test/SemaCXX/using-decl-templates.cpp | 2 +- test/SemaTemplate/template-id-expr.cpp | 6 ++++++ 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 86443871c3d..b54fad59d9a 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -4310,7 +4310,7 @@ def err_redefinition_different_typedef : Error< "%select{typedef|type alias|type alias template}0 " "redefinition with different types%diff{ ($ vs $)|}1,2">; def err_tag_reference_non_tag : Error< - "elaborated type refers to %select{a non-tag type|a typedef|a type alias|a template|a type alias template}0">; + "elaborated type refers to %select{a non-tag type|a typedef|a type alias|a template|a type alias template|a template template argument}0">; def err_tag_reference_conflict : Error< "implicit declaration introduced by elaborated type conflicts with " "%select{a declaration|a typedef|a type alias|a template}0 of the same name">; diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 90770498d1d..01442aa2793 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -7270,15 +7270,21 @@ Sema::ActOnExplicitInstantiation(Scope *S, assert(Kind != TTK_Enum && "Invalid enum tag in class template explicit instantiation!"); - if (isa(TD)) { - Diag(KWLoc, diag::err_tag_reference_non_tag) << Kind; - Diag(TD->getTemplatedDecl()->getLocation(), - diag::note_previous_use); + ClassTemplateDecl *ClassTemplate = dyn_cast(TD); + + if (!ClassTemplate) { + unsigned ErrorKind = 0; + if (isa(TD)) { + ErrorKind = 4; + } else if (isa(TD)) { + ErrorKind = 5; + } + + Diag(TemplateNameLoc, diag::err_tag_reference_non_tag) << ErrorKind; + Diag(TD->getLocation(), diag::note_previous_use); return true; } - ClassTemplateDecl *ClassTemplate = cast(TD); - if (!isAcceptableTagRedeclaration(ClassTemplate->getTemplatedDecl(), Kind, /*isDefinition*/false, KWLoc, ClassTemplate->getIdentifier())) { diff --git a/test/SemaCXX/using-decl-templates.cpp b/test/SemaCXX/using-decl-templates.cpp index 8314688bcbc..d766bb3ac6b 100644 --- a/test/SemaCXX/using-decl-templates.cpp +++ b/test/SemaCXX/using-decl-templates.cpp @@ -90,5 +90,5 @@ namespace aliastemplateinst { template struct A { }; template using APtr = A; // expected-note{{previous use is here}} - template struct APtr; // expected-error{{elaborated type refers to a non-tag type}} + template struct APtr; // expected-error{{elaborated type refers to a type alias template}} } diff --git a/test/SemaTemplate/template-id-expr.cpp b/test/SemaTemplate/template-id-expr.cpp index 4416f92723a..499d289ee67 100644 --- a/test/SemaTemplate/template-id-expr.cpp +++ b/test/SemaTemplate/template-id-expr.cpp @@ -96,3 +96,9 @@ void f5() { } template void f5<0>(); // expected-note {{in instantiation of function template specialization 'f5<0>' requested here}} + +class C {}; +template