r193549 - Allow a new syntax in a module requires-declaration:
Richard Smith
richard-llvm at metafoo.co.uk
Mon Oct 28 15:18:19 PDT 2013
Author: rsmith
Date: Mon Oct 28 17:18:19 2013
New Revision: 193549
URL: http://llvm.org/viewvc/llvm-project?rev=193549&view=rev
Log:
Allow a new syntax in a module requires-declaration:
requires ! feature
The purpose of this is to allow (for instance) the module map for /usr/include
to exclude <tgmath.h> and <complex.h> when building in C++ (these headers are
instead provided by the C++ standard library in this case, and the glibc C
<tgmath.h> header would otherwise try to include <complex.h>, resulting in a
module cycle).
Added:
cfe/trunk/test/Modules/Inputs/DependsOnModule.framework/Headers/not_cxx.h
cfe/trunk/test/Modules/Inputs/DependsOnModule.framework/Headers/not_objc.h
cfe/trunk/test/Modules/requires.mm
Modified:
cfe/trunk/docs/Modules.rst
cfe/trunk/include/clang/Basic/DiagnosticFrontendKinds.td
cfe/trunk/include/clang/Basic/Module.h
cfe/trunk/lib/Basic/Module.cpp
cfe/trunk/lib/Frontend/CompilerInstance.cpp
cfe/trunk/lib/Frontend/FrontendActions.cpp
cfe/trunk/lib/Lex/ModuleMap.cpp
cfe/trunk/lib/Serialization/ASTReader.cpp
cfe/trunk/lib/Serialization/ASTWriter.cpp
cfe/trunk/test/Index/index-module.m
cfe/trunk/test/Modules/Inputs/DependsOnModule.framework/module.map
cfe/trunk/test/Modules/requires.m
Modified: cfe/trunk/docs/Modules.rst
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/Modules.rst?rev=193549&r1=193548&r2=193549&view=diff
==============================================================================
--- cfe/trunk/docs/Modules.rst (original)
+++ cfe/trunk/docs/Modules.rst Mon Oct 28 17:18:19 2013
@@ -320,9 +320,12 @@ A *requires-declaration* specifies the r
``requires`` *feature-list*
*feature-list*:
- *identifier* (',' *identifier*)*
+ *feature* (',' *feature*)*
-The requirements clause allows specific modules or submodules to specify that they are only accessible with certain language dialects or on certain platforms. The feature list is a set of identifiers, defined below. If any of the features is not available in a given translation unit, that translation unit shall not import the module.
+ *feature*:
+ ``!``:sub:`opt` *identifier*
+
+The requirements clause allows specific modules or submodules to specify that they are only accessible with certain language dialects or on certain platforms. The feature list is a set of identifiers, defined below. If any of the features is not available in a given translation unit, that translation unit shall not import the module. The optional ``!`` indicates that a feature is incompatible with the module.
The following features are defined:
Modified: cfe/trunk/include/clang/Basic/DiagnosticFrontendKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticFrontendKinds.td?rev=193549&r1=193548&r2=193549&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticFrontendKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticFrontendKinds.td Mon Oct 28 17:18:19 2013
@@ -136,7 +136,8 @@ def err_no_submodule_suggest : Error<
"no submodule named %0 in module '%1'; did you mean '%2'?">;
def warn_missing_submodule : Warning<"missing submodule '%0'">,
InGroup<IncompleteUmbrella>;
-def err_module_unavailable : Error<"module '%0' requires feature '%1'">;
+def err_module_unavailable : Error<
+ "module '%0' %select{is incompatible with|requires}1 feature '%2'">;
def warn_module_config_macro_undef : Warning<
"%select{definition|#undef}0 of configuration macro '%1' has no effect on "
"the import of '%2'; pass '%select{-D%1=...|-U%1}0' on the command line "
Modified: cfe/trunk/include/clang/Basic/Module.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Module.h?rev=193549&r1=193548&r2=193549&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Module.h (original)
+++ cfe/trunk/include/clang/Basic/Module.h Mon Oct 28 17:18:19 2013
@@ -38,6 +38,7 @@ class FileEntry;
class FileManager;
class LangOptions;
class TargetInfo;
+class IdentifierInfo;
/// \brief Describes the name of a module.
typedef SmallVector<std::pair<std::string, SourceLocation>, 2> ModuleId;
@@ -89,12 +90,15 @@ public:
/// \brief The headers that are private to this module.
llvm::SmallVector<const FileEntry *, 2> PrivateHeaders;
+ /// \brief An individual requirement: a feature name and a flag indicating
+ /// the required state of that feature.
+ typedef std::pair<std::string, bool> Requirement;
+
/// \brief The set of language features required to use this module.
///
- /// If any of these features is not present, the \c IsAvailable bit
- /// will be false to indicate that this (sub)module is not
- /// available.
- SmallVector<std::string, 2> Requires;
+ /// If any of these requirements are not available, the \c IsAvailable bit
+ /// will be false to indicate that this (sub)module is not available.
+ SmallVector<Requirement, 2> Requirements;
/// \brief Whether this module is available in the current
/// translation unit.
@@ -267,12 +271,12 @@ public:
///
/// \param Target The target options used for the current translation unit.
///
- /// \param Feature If this module is unavailable, this parameter
- /// will be set to one of the features that is required for use of
- /// this module (but is not available).
+ /// \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.
bool isAvailable(const LangOptions &LangOpts,
const TargetInfo &Target,
- StringRef &Feature) const;
+ Requirement &Req) const;
/// \brief Determine whether this module is a submodule.
bool isSubModule() const { return Parent != 0; }
@@ -366,12 +370,16 @@ public:
/// \param Feature The feature that is required by this module (and
/// its submodules).
///
+ /// \param RequiredState The required state of this feature: \c true
+ /// if it must be present, \c false if it must be absent.
+ ///
/// \param LangOpts The set of language options that will be used to
/// evaluate the availability of this feature.
///
/// \param Target The target options that will be used to evaluate the
/// availability of this feature.
- void addRequirement(StringRef Feature, const LangOptions &LangOpts,
+ void addRequirement(StringRef Feature, bool RequiredState,
+ const LangOptions &LangOpts,
const TargetInfo &Target);
/// \brief Find the submodule with the given name.
Modified: cfe/trunk/lib/Basic/Module.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Basic/Module.cpp?rev=193549&r1=193548&r2=193549&view=diff
==============================================================================
--- cfe/trunk/lib/Basic/Module.cpp (original)
+++ cfe/trunk/lib/Basic/Module.cpp Mon Oct 28 17:18:19 2013
@@ -11,6 +11,7 @@
// code.
//
//===----------------------------------------------------------------------===//
+
#include "clang/Basic/Module.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/LangOptions.h"
@@ -20,6 +21,7 @@
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
+
using namespace clang;
Module::Module(StringRef Name, SourceLocation DefinitionLoc, Module *Parent,
@@ -65,16 +67,17 @@ static bool hasFeature(StringRef Feature
.Default(Target.hasFeature(Feature));
}
-bool
+bool
Module::isAvailable(const LangOptions &LangOpts, const TargetInfo &Target,
- StringRef &Feature) const {
+ Requirement &Req) const {
if (IsAvailable)
return true;
for (const Module *Current = this; Current; Current = Current->Parent) {
- for (unsigned I = 0, N = Current->Requires.size(); I != N; ++I) {
- if (!hasFeature(Current->Requires[I], LangOpts, Target)) {
- Feature = Current->Requires[I];
+ for (unsigned I = 0, N = Current->Requirements.size(); I != N; ++I) {
+ if (hasFeature(Current->Requirements[I].first, LangOpts, Target) !=
+ Current->Requirements[I].second) {
+ Req = Current->Requirements[I];
return false;
}
}
@@ -143,12 +146,13 @@ ArrayRef<const FileEntry *> Module::getT
return llvm::makeArrayRef(TopHeaders.begin(), TopHeaders.end());
}
-void Module::addRequirement(StringRef Feature, const LangOptions &LangOpts,
+void Module::addRequirement(StringRef Feature, bool RequiredState,
+ const LangOptions &LangOpts,
const TargetInfo &Target) {
- Requires.push_back(Feature);
+ Requirements.push_back(Requirement(Feature, RequiredState));
// If this feature is currently available, we're done.
- if (hasFeature(Feature, LangOpts, Target))
+ if (hasFeature(Feature, LangOpts, Target) == RequiredState)
return;
if (!IsAvailable)
@@ -275,13 +279,15 @@ void Module::print(raw_ostream &OS, unsi
OS << " {\n";
- if (!Requires.empty()) {
+ if (!Requirements.empty()) {
OS.indent(Indent + 2);
OS << "requires ";
- for (unsigned I = 0, N = Requires.size(); I != N; ++I) {
+ for (unsigned I = 0, N = Requirements.size(); I != N; ++I) {
if (I)
OS << ", ";
- OS << Requires[I];
+ if (!Requirements[I].second)
+ OS << "!";
+ OS << Requirements[I].first;
}
OS << "\n";
}
Modified: cfe/trunk/lib/Frontend/CompilerInstance.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/CompilerInstance.cpp?rev=193549&r1=193548&r2=193549&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/CompilerInstance.cpp (original)
+++ cfe/trunk/lib/Frontend/CompilerInstance.cpp Mon Oct 28 17:18:19 2013
@@ -1359,11 +1359,11 @@ CompilerInstance::loadModule(SourceLocat
}
// Check whether this module is available.
- StringRef Feature;
- if (!Module->isAvailable(getLangOpts(), getTarget(), Feature)) {
+ clang::Module::Requirement Requirement;
+ if (!Module->isAvailable(getLangOpts(), getTarget(), Requirement)) {
getDiagnostics().Report(ImportLoc, diag::err_module_unavailable)
<< Module->getFullModuleName()
- << Feature
+ << Requirement.second << Requirement.first
<< SourceRange(Path.front().second, Path.back().second);
LastModuleImportLoc = ImportLoc;
LastModuleImportResult = ModuleLoadResult();
Modified: cfe/trunk/lib/Frontend/FrontendActions.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/FrontendActions.cpp?rev=193549&r1=193548&r2=193549&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/FrontendActions.cpp (original)
+++ cfe/trunk/lib/Frontend/FrontendActions.cpp Mon Oct 28 17:18:19 2013
@@ -246,11 +246,11 @@ bool GenerateModuleAction::BeginSourceFi
}
// Check whether we can build this module at all.
- StringRef Feature;
- if (!Module->isAvailable(CI.getLangOpts(), CI.getTarget(), Feature)) {
+ clang::Module::Requirement Requirement;
+ if (!Module->isAvailable(CI.getLangOpts(), CI.getTarget(), Requirement)) {
CI.getDiagnostics().Report(diag::err_module_unavailable)
<< Module->getFullModuleName()
- << Feature;
+ << Requirement.second << Requirement.first;
return false;
}
Modified: cfe/trunk/lib/Lex/ModuleMap.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Lex/ModuleMap.cpp?rev=193549&r1=193548&r2=193549&view=diff
==============================================================================
--- cfe/trunk/lib/Lex/ModuleMap.cpp (original)
+++ cfe/trunk/lib/Lex/ModuleMap.cpp Mon Oct 28 17:18:19 2013
@@ -773,6 +773,7 @@ namespace clang {
EndOfFile,
HeaderKeyword,
Identifier,
+ Exclaim,
ExcludeKeyword,
ExplicitKeyword,
ExportKeyword,
@@ -967,6 +968,10 @@ retry:
Tok.Kind = MMToken::Star;
break;
+ case tok::exclaim:
+ Tok.Kind = MMToken::Exclaim;
+ break;
+
case tok::string_literal: {
if (LToken.hasUDSuffix()) {
Diags.Report(LToken.getLocation(), diag::err_invalid_string_udl);
@@ -1392,8 +1397,11 @@ void ModuleMapParser::parseExternModuleD
/// 'requires' feature-list
///
/// feature-list:
-/// identifier ',' feature-list
-/// identifier
+/// feature ',' feature-list
+/// feature
+///
+/// feature:
+/// '!'[opt] identifier
void ModuleMapParser::parseRequiresDecl() {
assert(Tok.is(MMToken::RequiresKeyword));
@@ -1402,6 +1410,12 @@ void ModuleMapParser::parseRequiresDecl(
// Parse the feature-list.
do {
+ bool RequiredState = true;
+ if (Tok.is(MMToken::Exclaim)) {
+ RequiredState = false;
+ consumeToken();
+ }
+
if (!Tok.is(MMToken::Identifier)) {
Diags.Report(Tok.getLocation(), diag::err_mmap_expected_feature);
HadError = true;
@@ -1413,7 +1427,8 @@ void ModuleMapParser::parseRequiresDecl(
consumeToken();
// Add this feature.
- ActiveModule->addRequirement(Feature, Map.LangOpts, *Map.Target);
+ ActiveModule->addRequirement(Feature, RequiredState,
+ Map.LangOpts, *Map.Target);
if (!Tok.is(MMToken::Comma))
break;
@@ -2077,6 +2092,7 @@ bool ModuleMapParser::parseModuleMapFile
case MMToken::Comma:
case MMToken::ConfigMacros:
case MMToken::Conflict:
+ case MMToken::Exclaim:
case MMToken::ExcludeKeyword:
case MMToken::ExportKeyword:
case MMToken::HeaderKeyword:
Modified: cfe/trunk/lib/Serialization/ASTReader.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReader.cpp?rev=193549&r1=193548&r2=193549&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTReader.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTReader.cpp Mon Oct 28 17:18:19 2013
@@ -3912,7 +3912,7 @@ bool ASTReader::ReadSubmoduleBlock(Modul
if (!CurrentModule)
break;
- CurrentModule->addRequirement(Blob, Context.getLangOpts(),
+ CurrentModule->addRequirement(Blob, Record[0], Context.getLangOpts(),
Context.getTargetInfo());
break;
}
Modified: cfe/trunk/lib/Serialization/ASTWriter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriter.cpp?rev=193549&r1=193548&r2=193549&view=diff
==============================================================================
--- cfe/trunk/lib/Serialization/ASTWriter.cpp (original)
+++ cfe/trunk/lib/Serialization/ASTWriter.cpp Mon Oct 28 17:18:19 2013
@@ -2278,7 +2278,8 @@ void ASTWriter::WriteSubmodules(Module *
Abbrev = new BitCodeAbbrev();
Abbrev->Add(BitCodeAbbrevOp(SUBMODULE_REQUIRES));
- Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Feature
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // State
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Feature
unsigned RequiresAbbrev = Stream.EmitAbbrev(Abbrev);
Abbrev = new BitCodeAbbrev();
@@ -2342,12 +2343,12 @@ void ASTWriter::WriteSubmodules(Module *
Stream.EmitRecordWithBlob(DefinitionAbbrev, Record, Mod->Name);
// Emit the requirements.
- for (unsigned I = 0, N = Mod->Requires.size(); I != N; ++I) {
+ for (unsigned I = 0, N = Mod->Requirements.size(); I != N; ++I) {
Record.clear();
Record.push_back(SUBMODULE_REQUIRES);
+ Record.push_back(Mod->Requirements[I].second);
Stream.EmitRecordWithBlob(RequiresAbbrev, Record,
- Mod->Requires[I].data(),
- Mod->Requires[I].size());
+ Mod->Requirements[I].first);
}
// Emit the umbrella header, if there is one.
Modified: cfe/trunk/test/Index/index-module.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Index/index-module.m?rev=193549&r1=193548&r2=193549&view=diff
==============================================================================
--- cfe/trunk/test/Index/index-module.m (original)
+++ cfe/trunk/test/Index/index-module.m Mon Oct 28 17:18:19 2013
@@ -21,11 +21,13 @@ int glob;
// CHECK-DMOD-NEXT: [ppIncludedFile]: [[DMOD_MODULE_H:.*/Modules/Inputs/DependsOnModule\.framework[/\\]Headers[/\\]DependsOnModule\.h]] | {{.*}} | hash loc: <invalid>
// CHECK-DMOD-NEXT: [ppIncludedFile]: {{.*}}/Modules/Inputs/Module.framework{{[/\\]}}Headers{{[/\\]}}Module.h | name: "Module/Module.h" | hash loc: {{.*}}/Modules/Inputs/DependsOnModule.framework{{[/\\]}}Headers{{[/\\]}}DependsOnModule.h:1:1 | isImport: 0 | isAngled: 1 | isModule: 1
// CHECK-DMOD-NEXT: [ppIncludedFile]: [[DMOD_OTHER_H:.*/Modules/Inputs/DependsOnModule\.framework[/\\]Headers[/\\]other\.h]] | {{.*}} | hash loc: <invalid>
+// CHECK-DMOD-NEXT: [ppIncludedFile]: [[DMOD_NOT_CXX_H:.*/Modules/Inputs/DependsOnModule\.framework[/\\]Headers[/\\]not_cxx\.h]] | {{.*}} | hash loc: <invalid>
// CHECK-DMOD-NEXT: [ppIncludedFile]: [[DMOD_SUB_H:.*/Modules/Inputs/DependsOnModule\.framework[/\\]Frameworks[/\\]SubFramework\.framework[/\\]Headers[/\\]SubFramework\.h]] | {{.*}} | hash loc: <invalid>
// CHECK-DMOD-NEXT: [ppIncludedFile]: [[DMOD_SUB_OTHER_H:.*/Modules/Inputs/DependsOnModule.framework[/\\]Frameworks/SubFramework\.framework/Headers/Other\.h]] | name: "SubFramework/Other.h" | hash loc: [[DMOD_SUB_H]]:1:1 | isImport: 0 | isAngled: 0
// CHECK-DMOD-NEXT: [ppIncludedFile]: [[DMOD_PRIVATE_H:.*/Modules/Inputs/DependsOnModule.framework[/\\]PrivateHeaders[/\\]DependsOnModulePrivate.h]] | {{.*}} | hash loc: <invalid>
// CHECK-DMOD-NEXT: [importedASTFile]: {{.*}}.cache{{[/\\]}}Module.pcm | loc: [[DMOD_MODULE_H]]:1:2 | name: "Module" | isImplicit: 1
// CHECK-DMOD-NEXT: [indexDeclaration]: kind: variable | name: depends_on_module_other | {{.*}} | loc: [[DMOD_OTHER_H]]:1:5
+// CHECK-DMOD-NEXT: [indexDeclaration]: kind: variable | name: template | {{.*}} | loc: [[DMOD_NOT_CXX_H]]:1:12
// CHECK-DMOD-NEXT: [importedASTFile]: {{.*}}.cache/DependsOnModule.pcm | loc: {{.*}}SubFramework.h:1:2 | name: "DependsOnModule.SubFramework.Other" | isImplicit: 1
// CHECK-DMOD-NEXT: [indexDeclaration]: kind: variable | name: sub_framework | {{.*}} | loc: [[DMOD_SUB_H]]:2:8
// CHECK-DMOD-NEXT: [indexDeclaration]: kind: variable | name: sub_framework_other | {{.*}} | loc: [[DMOD_SUB_OTHER_H]]:1:9
Added: cfe/trunk/test/Modules/Inputs/DependsOnModule.framework/Headers/not_cxx.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/DependsOnModule.framework/Headers/not_cxx.h?rev=193549&view=auto
==============================================================================
--- cfe/trunk/test/Modules/Inputs/DependsOnModule.framework/Headers/not_cxx.h (added)
+++ cfe/trunk/test/Modules/Inputs/DependsOnModule.framework/Headers/not_cxx.h Mon Oct 28 17:18:19 2013
@@ -0,0 +1 @@
+extern int template;
Added: cfe/trunk/test/Modules/Inputs/DependsOnModule.framework/Headers/not_objc.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/DependsOnModule.framework/Headers/not_objc.h?rev=193549&view=auto
==============================================================================
--- cfe/trunk/test/Modules/Inputs/DependsOnModule.framework/Headers/not_objc.h (added)
+++ cfe/trunk/test/Modules/Inputs/DependsOnModule.framework/Headers/not_objc.h Mon Oct 28 17:18:19 2013
@@ -0,0 +1 @@
+int NSObject;
Modified: cfe/trunk/test/Modules/Inputs/DependsOnModule.framework/module.map
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/Inputs/DependsOnModule.framework/module.map?rev=193549&r1=193548&r2=193549&view=diff
==============================================================================
--- cfe/trunk/test/Modules/Inputs/DependsOnModule.framework/module.map (original)
+++ cfe/trunk/test/Modules/Inputs/DependsOnModule.framework/module.map Mon Oct 28 17:18:19 2013
@@ -8,6 +8,14 @@ framework module DependsOnModule {
requires cplusplus
header "cxx_other.h"
}
+ explicit module NotCXX {
+ requires !cplusplus
+ header "not_cxx.h"
+ }
+ explicit module NotObjC {
+ requires !objc
+ header "not_objc.h"
+ }
explicit framework module SubFramework {
umbrella header "SubFramework.h"
Modified: cfe/trunk/test/Modules/requires.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/requires.m?rev=193549&r1=193548&r2=193549&view=diff
==============================================================================
--- cfe/trunk/test/Modules/requires.m (original)
+++ cfe/trunk/test/Modules/requires.m Mon Oct 28 17:18:19 2013
@@ -2,4 +2,5 @@
// RUN: %clang_cc1 -Wauto-import -fmodules-cache-path=%t -fmodules -F %S/Inputs %s -verify
@import DependsOnModule.CXX; // expected-error{{module 'DependsOnModule.CXX' requires feature 'cplusplus'}}
-
+ at import DependsOnModule.NotCXX;
+ at import DependsOnModule.NotObjC; // expected-error{{module 'DependsOnModule.NotObjC' is incompatible with feature 'objc'}}
Added: cfe/trunk/test/Modules/requires.mm
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Modules/requires.mm?rev=193549&view=auto
==============================================================================
--- cfe/trunk/test/Modules/requires.mm (added)
+++ cfe/trunk/test/Modules/requires.mm Mon Oct 28 17:18:19 2013
@@ -0,0 +1,6 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -Wauto-import -fmodules-cache-path=%t -fmodules -F %S/Inputs %s -verify
+
+ at import DependsOnModule.CXX;
+ at import DependsOnModule.NotCXX; // expected-error{{module 'DependsOnModule.NotCXX' is incompatible with feature 'cplusplus'}}
+ at import DependsOnModule.NotObjC; // expected-error{{module 'DependsOnModule.NotObjC' is incompatible with feature 'objc'}}
More information about the cfe-commits
mailing list