iains updated this revision to Diff 438968.
iains marked 2 inline comments as done.
iains added a comment.
reabsed onto latest version of D113545 <https://reviews.llvm.org/D113545>.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D126694/new/
https://reviews.llvm.org/D126694
Files:
clang/include/clang/AST/DeclBase.h
clang/include/clang/Sema/Sema.h
clang/lib/AST/Decl.cpp
clang/lib/AST/TextNodeDumper.cpp
clang/lib/Sema/Sema.cpp
clang/lib/Sema/SemaDecl.cpp
clang/lib/Sema/SemaDeclAttr.cpp
clang/lib/Sema/SemaDeclCXX.cpp
clang/lib/Sema/SemaExpr.cpp
clang/lib/Sema/SemaInit.cpp
clang/lib/Sema/SemaLambda.cpp
clang/lib/Sema/SemaModule.cpp
clang/lib/Sema/SemaOpenMP.cpp
clang/lib/Sema/SemaStmt.cpp
clang/lib/Sema/SemaStmtAsm.cpp
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
clang/lib/Serialization/ASTReaderDecl.cpp
clang/test/AST/ast-dump-decl.c
clang/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp
clang/test/Modules/cxx-templates.cpp
clang/test/Modules/cxx20-10-4-ex2.cpp
clang/test/Modules/explicitly-specialized-template.cpp
clang/test/Modules/template-function-specialization.cpp
Index: clang/test/Modules/template-function-specialization.cpp
===================================================================
--- clang/test/Modules/template-function-specialization.cpp
+++ clang/test/Modules/template-function-specialization.cpp
@@ -51,7 +51,9 @@
foo2<short>(); // expected-error {{missing '#include'; 'foo2' must be declared before it is used}}
// expected-note@* {{declaration here is not visible}}
foo2<int>(); // expected-error {{missing '#include'; 'foo2' must be declared before it is used}}
+ // expected-error@-1 {{missing '#include'; explicit specialization of 'foo2<int>' must be declared before it is used}}
// expected-note@* {{declaration here is not visible}}
+ // expected-note@* {{explicit specialization declared here is not reachable}}
foo3<short>();
foo3<int>();
Index: clang/test/Modules/explicitly-specialized-template.cpp
===================================================================
--- clang/test/Modules/explicitly-specialized-template.cpp
+++ clang/test/Modules/explicitly-specialized-template.cpp
@@ -42,6 +42,6 @@
foo<int> f; // expected-error {{'foo' must be declared before it is used}}
// expected-note@* {{declaration here is not visible}}
int bar() {
- X<int> x;
- return x.print();
+ X<int> y;
+ return y.print();
}
Index: clang/test/Modules/cxx20-10-4-ex2.cpp
===================================================================
--- /dev/null
+++ clang/test/Modules/cxx20-10-4-ex2.cpp
@@ -0,0 +1,60 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: cd %t
+
+// RUN: %clang_cc1 -std=c++20 std-10-4-ex2-interface.cpp \
+// RUN: -emit-module-interface -o M.pcm -Wno-unused-value
+// RUN: %clang_cc1 -std=c++20 std-10-4-ex2-implementation.cpp \
+// RUN: -fmodule-file=M.pcm -fsyntax-only -verify
+//--- std-10-4-ex2.h
+
+namespace N {
+struct X {};
+int d();
+int e();
+inline int f(X, int = d()) { return e(); }
+int g(X);
+int h(X);
+} // namespace N
+
+//--- std-10-4-ex2-interface.cpp
+
+module;
+
+#include "std-10-4-ex2.h"
+
+export module M;
+
+template <typename T> int use_f() {
+ N::X x; // N::X, N, and :: are decl-reachable from use_f
+ return f(x, 123); // N::f is decl-reachable from use_f,
+ // N::e is indirectly decl-reachable from use_f
+ // because it is decl-reachable from N::f, and
+ // N::d is decl-reachable from use_f
+ // because it is decl-reachable from N::f
+ // even though it is not used in this call
+}
+
+template <typename T> int use_g() {
+ N::X x; // N::X, N, and :: are decl-reachable from use_g
+ return g((T(), x)); // N::g is not decl-reachable from use_g
+}
+
+template <typename T> int use_h() {
+ N::X x; // N::X, N, and :: are decl-reachable from use_h
+ return h((T(), x)); // N::h is not decl-reachable from use_h, but
+ // N::h is decl-reachable from use_h<int>
+}
+
+int k = use_h<int>();
+// use_h<int> is decl-reachable from k, so
+// N::h is decl-reachable from k
+
+//--- std-10-4-ex2-implementation.cpp
+
+module M;
+
+int a = use_f<int>();
+int b = use_g<int>(); // [email protected]:20 {{use of undeclared identifier 'g'}}
+ // expected-note@-1 {{in instantiation of function template specialization 'use_g<int>' requested here}}
+int c = use_h<int>();
Index: clang/test/Modules/cxx-templates.cpp
===================================================================
--- clang/test/Modules/cxx-templates.cpp
+++ clang/test/Modules/cxx-templates.cpp
@@ -252,7 +252,7 @@
// CHECK-DUMP: ClassTemplateDecl {{.*}} <{{.*[/\\]}}cxx-templates-common.h:1:1, {{.*}}> col:{{.*}} in cxx_templates_common SomeTemplate
// CHECK-DUMP: ClassTemplateSpecializationDecl {{.*}} prev {{.*}} SomeTemplate
// CHECK-DUMP-NEXT: TemplateArgument type 'char[2]'
-// CHECK-DUMP: ClassTemplateSpecializationDecl {{.*}} SomeTemplate definition
+// CHECK-DUMP: ClassTemplateSpecializationDecl {{.*}} SomeTemplate Visible definition
// CHECK-DUMP-NEXT: DefinitionData
// CHECK-DUMP-NEXT: DefaultConstructor
// CHECK-DUMP-NEXT: CopyConstructor
@@ -263,7 +263,7 @@
// CHECK-DUMP-NEXT: TemplateArgument type 'char[2]'
// CHECK-DUMP: ClassTemplateSpecializationDecl {{.*}} prev {{.*}} SomeTemplate
// CHECK-DUMP-NEXT: TemplateArgument type 'char[1]'
-// CHECK-DUMP: ClassTemplateSpecializationDecl {{.*}} SomeTemplate definition
+// CHECK-DUMP: ClassTemplateSpecializationDecl {{.*}} SomeTemplate Visible definition
// CHECK-DUMP-NEXT: DefinitionData
// CHECK-DUMP-NEXT: DefaultConstructor
// CHECK-DUMP-NEXT: CopyConstructor
Index: clang/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp
===================================================================
--- clang/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp
+++ clang/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp
@@ -29,9 +29,8 @@
#endif
void test_early() {
- in_header = 1; // expected-error {{missing '#include "foo.h"'; 'in_header' must be declared before it is used}}
- // expected-note@* {{not visible}}
-
+ in_header = 1; // expected-error {{missing '#include "foo.h"'; 'in_header' must be declared before it is used}}
+ // [email protected]:3 {{declaration here is not visible}}
global_module_fragment = 1; // expected-error {{missing '#include'; 'global_module_fragment' must be declared before it is used}}
// [email protected]:16 {{not visible}}
@@ -57,9 +56,8 @@
#endif
void test_late() {
- in_header = 1; // expected-error {{missing '#include "foo.h"'; 'in_header' must be declared before it is used}}
- // expected-note@* {{not visible}}
-
+ in_header = 1; // expected-error {{missing '#include "foo.h"'; 'in_header' must be declared before it is used}}
+ // [email protected]:3 {{declaration here is not visible}}
global_module_fragment = 1; // expected-error {{missing '#include'; 'global_module_fragment' must be declared before it is used}}
// [email protected]:16 {{not visible}}
Index: clang/test/AST/ast-dump-decl.c
===================================================================
--- clang/test/AST/ast-dump-decl.c
+++ clang/test/AST/ast-dump-decl.c
@@ -1,16 +1,16 @@
// Test without serialization:
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -Wno-strict-prototypes -ast-dump -ast-dump-filter Test %s \
-// RUN: | FileCheck --strict-whitespace %s
+// RUN: | FileCheck --strict-whitespace %s -check-prefix CHECK-NOMODULE
//
// Test with serialization:
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -Wno-strict-prototypes -emit-pch -o %t %s
// RUN: %clang_cc1 -x c -triple x86_64-unknown-unknown -Wno-strict-prototypes -include-pch %t \
// RUN: -ast-dump-all -ast-dump-filter Test /dev/null \
// RUN: | sed -e "s/ <undeserialized declarations>//" -e "s/ imported//" \
-// RUN: | FileCheck --strict-whitespace %s
+// RUN: | FileCheck --strict-whitespace %s -check-prefix CHECK-NOMODULE
//
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -Wno-strict-prototypes -ast-dump %s \
-// RUN: | FileCheck -check-prefix CHECK-TU --strict-whitespace %s
+// RUN: | FileCheck -check-prefix CHECK-TU --strict-whitespace %s -check-prefix CHECK-NOMODULE
//
// RUN: %clang_cc1 -fmodules -fmodules-local-submodule-visibility -fmodule-name=X \
// RUN: -triple x86_64-unknown-unknown -Wno-strict-prototypes -fmodule-map-file=%S/Inputs/module.modulemap \
@@ -166,13 +166,16 @@
// CHECK-MODULE: FieldDecl{{.*}} TestFieldDeclPrivate 'int' __module_private__
int TestVarDecl;
-// CHECK: VarDecl{{.*}} TestVarDecl 'int'
+// CHECK-NOMODULE: VarDecl{{.*}} TestVarDecl 'int'
+// CHECK-MODULE: VarDecl{{.*}} TestVarDecl 'int' VisibleWhenImported
extern int TestVarDeclSC;
-// CHECK: VarDecl{{.*}} TestVarDeclSC 'int' extern
+// CHECK-NOMODULE: VarDecl{{.*}} TestVarDeclSC 'int' extern
+// CHECK-MODULE: VarDecl{{.*}} TestVarDeclSC 'int' extern VisibleWhenImported
__thread int TestVarDeclThread;
-// CHECK: VarDecl{{.*}} TestVarDeclThread 'int' tls{{$}}
+// CHECK-NOMODULE: VarDecl{{.*}} TestVarDeclThread 'int' tls{{$}}
+// CHECK-MODULE: VarDecl{{.*}} TestVarDeclThread 'int' tls{{$}} VisibleWhenImported
__module_private__ int TestVarDeclPrivate;
// CHECK-MODULE: VarDecl{{.*}} TestVarDeclPrivate 'int' __module_private__
Index: clang/lib/Serialization/ASTReaderDecl.cpp
===================================================================
--- clang/lib/Serialization/ASTReaderDecl.cpp
+++ clang/lib/Serialization/ASTReaderDecl.cpp
@@ -621,19 +621,18 @@
case Decl::ModuleOwnershipKind::VisibleWhenImported:
case Decl::ModuleOwnershipKind::ReachableWhenImported:
case Decl::ModuleOwnershipKind::ModulePrivate:
+ case Decl::ModuleOwnershipKind::ModuleDiscardable:
break;
-
- default:
- llvm_unreachable("Unknown ModuleOwnership kind");
}
D->setModuleOwnershipKind(ModuleOwnership);
// Store the owning submodule ID in the declaration.
D->setOwningModuleID(SubmoduleID);
- if (ModulePrivate) {
- // Module-private declarations are never visible, so there is no work to
- // do.
+ if (ModulePrivate ||
+ ModuleOwnership == Decl::ModuleOwnershipKind::ModuleDiscardable) {
+ // Module-private and unreachable declarations are never visible, so
+ // there is no work to do.
} else if (Reader.getContext().getLangOpts().ModulesLocalVisibility) {
// If local visibility is being tracked, this declaration will become
// hidden and visible as the owning module does.
@@ -644,6 +643,8 @@
else
Reader.HiddenNamesMap[Owner].push_back(D);
}
+ } else if (ModuleOwnership == Decl::ModuleOwnershipKind::ModuleDiscardable) {
+ D->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ModuleDiscardable);
} else if (ModulePrivate) {
D->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ModulePrivate);
}
Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
===================================================================
--- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1004,7 +1004,7 @@
SemaRef.inferGslPointerAttribute(Typedef);
Typedef->setAccess(D->getAccess());
- Typedef->setReferenced(D->isReferenced());
+ SemaRef.setDeclReferenced(Typedef, D->isReferenced());
return Typedef;
}
@@ -1069,7 +1069,7 @@
Decl *TemplateDeclInstantiator::VisitBindingDecl(BindingDecl *D) {
auto *NewBD = BindingDecl::Create(SemaRef.Context, Owner, D->getLocation(),
D->getIdentifier());
- NewBD->setReferenced(D->isReferenced());
+ SemaRef.setDeclReferenced(NewBD, D->isReferenced());
SemaRef.CurrentInstantiationScope->InstantiatedLocal(D, NewBD);
return NewBD;
}
@@ -5213,8 +5213,8 @@
if (!OldVar->isStaticDataMember()) {
if (OldVar->isUsed(false))
- NewVar->setIsUsed();
- NewVar->setReferenced(OldVar->isReferenced());
+ setDeclIsUsed(NewVar);
+ setDeclReferenced(static_cast<Decl *>(NewVar), OldVar->isReferenced());
}
InstantiateAttrs(TemplateArgs, OldVar, NewVar, LateAttrs, StartingScope);
Index: clang/lib/Sema/SemaStmtAsm.cpp
===================================================================
--- clang/lib/Sema/SemaStmtAsm.cpp
+++ clang/lib/Sema/SemaStmtAsm.cpp
@@ -956,7 +956,7 @@
if (Label->isMSAsmLabel()) {
// If we have previously created this label implicitly, mark it as used.
- Label->markUsed(Context);
+ markDeclUsed(Label, Context);
} else {
// Otherwise, insert it, but only resolve it if we have seen the label itself.
std::string InternalName;
Index: clang/lib/Sema/SemaStmt.cpp
===================================================================
--- clang/lib/Sema/SemaStmt.cpp
+++ clang/lib/Sema/SemaStmt.cpp
@@ -2751,7 +2751,7 @@
if (RangeVarType->isDependentType()) {
// The range is implicitly used as a placeholder when it is dependent.
- RangeVar->markUsed(Context);
+ markDeclUsed(static_cast<Decl *>(RangeVar), Context);
// Deduce any 'auto's in the loop variable as 'DependentTy'. We'll fill
// them in properly when we instantiate the loop.
@@ -3266,7 +3266,7 @@
SourceLocation LabelLoc,
LabelDecl *TheDecl) {
setFunctionHasBranchIntoScope();
- TheDecl->markUsed(Context);
+ markDeclUsed(TheDecl, Context);
return new (Context) GotoStmt(TheDecl, GotoLoc, LabelLoc);
}
Index: clang/lib/Sema/SemaOpenMP.cpp
===================================================================
--- clang/lib/Sema/SemaOpenMP.cpp
+++ clang/lib/Sema/SemaOpenMP.cpp
@@ -1455,7 +1455,7 @@
SourceLocation Loc,
bool RefersToCapture = false) {
D->setReferenced();
- D->markUsed(S.Context);
+ S.markDeclUsed(D, S.Context);
return DeclRefExpr::Create(S.getASTContext(), NestedNameSpecifierLoc(),
SourceLocation(), D, RefersToCapture, Loc, Ty,
VK_LValue);
@@ -3004,7 +3004,7 @@
// Mark variable as used.
VD->setReferenced();
- VD->markUsed(Context);
+ markDeclUsed(static_cast<Decl *>(VD), Context);
QualType QType = VD->getType();
if (QType->isDependentType() || QType->isInstantiationDependentType()) {
Index: clang/lib/Sema/SemaModule.cpp
===================================================================
--- clang/lib/Sema/SemaModule.cpp
+++ clang/lib/Sema/SemaModule.cpp
@@ -12,10 +12,14 @@
//===----------------------------------------------------------------------===//
#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/DeclFriend.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/SemaInternal.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Format.h"
+
using namespace clang;
using namespace sema;
@@ -83,8 +87,6 @@
return nullptr;
}
- // We start in the global module; all those declarations are implicitly
- // module-private (though they do not have module linkage).
Module *GlobalModule =
PushGlobalModuleFragment(ModuleLoc, /*IsImplicit=*/false);
@@ -96,8 +98,10 @@
// provide declarations that are attached to the global module and usable
// within the module unit.
//
- // So the declations in the global module shouldn't be visible by default.
- TU->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ReachableWhenImported);
+ // We are expected to elide GMF decls that are not used in the purview of the
+ // named module. In order to do that, create GMF decls with a marker that is
+ // updated when a decl is used.
+ TU->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ModuleDiscardable);
TU->setLocalOwningModule(GlobalModule);
// FIXME: Consider creating an explicit representation of this declaration.
@@ -901,6 +905,20 @@
diagExportedUnnamedDecl(*this, UnnamedDeclKind::Context, Child,
BlockStart);
}
+ if (GlobalModuleFragment) {
+ // An export must be in module purview, so mark any used GMF content
+ // as not discardable, and the current Decl as visible.
+ if (auto *USD = dyn_cast<UsingShadowDecl>(Child)) {
+ NamedDecl *Target = USD->getUnderlyingDecl();
+ if (Target->hasOwningModule() &&
+ Target->getOwningModule()->isGlobalModule()) {
+ // Handle using declarations that make GMF content visible.
+ markGMFDeclsReachableFrom(Target, /*MakeVisible*/ true);
+ }
+ } else {
+ markGMFDeclsReachableFrom(Child, /*MakeVisible*/ true);
+ }
+ }
}
}
@@ -935,3 +953,171 @@
"left the wrong module scope, which is not global module fragment");
ModuleScopes.pop_back();
}
+
+// A helper to process a used type to find GMF reachable decls which it uses.
+
+void Sema::findGMFReachableDeclsForType(const Type *T) {
+ assert(T && "missing type pointer?");
+ // Only visit each Type once.
+ if (!SeenTypes.insert(T).second)
+ return;
+ // Return as early as possible if this is not an entity we would need to mark
+ // but typedefs of built-in types need to be caught first (since the typedef
+ // could be in the GMF).
+ if (auto *TD = dyn_cast<TypedefType>(T)) {
+ markGMFDeclsReachableFrom(TD->getDecl());
+ } else if (T->isBuiltinType()) {
+ return;
+ } else if (auto *TST = dyn_cast<TemplateSpecializationType>(T)) {
+ if (auto *RT = TST->getAsCXXRecordDecl())
+ markGMFDeclsReachableFrom(RT);
+ TemplateDecl *TD = TST->getTemplateName().getAsTemplateDecl();
+ markGMFDeclsReachableFrom(TD);
+ } else if (auto *RD = T->getAsCXXRecordDecl()) {
+ markGMFDeclsReachableFrom(RD);
+ } else if (auto *PT = dyn_cast<PointerType>(T)) {
+ return;
+ } else {
+ llvm::dbgs() << llvm::format("unhandled type: %p ", T);
+ T->dump();
+ }
+}
+
+// A helper to recurse through a stmt/expr tree finding expressions with
+// content that makes GMF decls reachable.
+
+void Sema::findGMFReachableDeclExprs(clang::Stmt *S) {
+
+ if (DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S)) {
+ markGMFDeclsReachableFrom(DR->getFoundDecl());
+ } else if (DependentScopeDeclRefExpr *DS =
+ dyn_cast<DependentScopeDeclRefExpr>(S)) {
+ auto *NNS = DS->getQualifier();
+ assert(NNS && (NNS->getKind() == NestedNameSpecifier::TypeSpec) &&
+ "not a type spec");
+ findGMFReachableDeclsForType(NNS->getAsType());
+ } else if (UnresolvedLookupExpr *ULE = dyn_cast<UnresolvedLookupExpr>(S)) {
+ for (auto *D : ULE->decls())
+ markGMFDeclsReachableFrom(D);
+ } else if (DeclStmt *DS = dyn_cast<DeclStmt>(S)) {
+ if (DS->isSingleDecl())
+ markGMFDeclsReachableFrom(DS->getSingleDecl());
+ else {
+ for (auto *D : DS->decls()) {
+ llvm::dbgs() << "EMD: ";
+ D->dump();
+ markGMFDeclsReachableFrom(D);
+ }
+ }
+ }
+
+ // Recurse into sub-expressions.
+ for (auto *CH : S->children())
+ findGMFReachableDeclExprs(CH);
+}
+
+// Starting from decl Orig (which is marked as reachable/visible if it is in
+// the GMF and currently marked as ModuleDiscardable), process other decls
+// reachable from this one and mark them too.
+
+void Sema::markGMFDeclsReachableFrom(Decl *Orig, bool MakeVisible) {
+ // Only visit each Decl once.
+ if (!SeenDecls.insert(Orig).second)
+ return;
+
+ // Do not alter the ownership unless it is ModuleDiscardable.
+ if (Orig->isModuleDiscardable()) {
+ assert(Orig->getOwningModule() &&
+ Orig->getOwningModule()->isGlobalModule() &&
+ "should not have a discardable decl outside the GMF");
+ Orig->setModuleOwnershipKind(
+ MakeVisible ? Decl::ModuleOwnershipKind::VisibleWhenImported
+ : Decl::ModuleOwnershipKind::ReachableWhenImported);
+ }
+
+ if (auto FT = dyn_cast<FunctionTemplateDecl>(Orig)) {
+ markGMFDeclsReachableFrom(FT->getTemplatedDecl());
+ for (auto *SP : FT->specializations())
+ markGMFDeclsReachableFrom(SP);
+ for (auto *TA : *FT->getTemplateParameters())
+ markGMFDeclsReachableFrom(TA);
+ return;
+ }
+
+ if (auto *FD = dyn_cast<FunctionDecl>(Orig)) {
+ findGMFReachableDeclsForType(FD->getReturnType().getTypePtr());
+ for (ParmVarDecl *P : FD->parameters())
+ markGMFDeclsReachableFrom(P);
+ const FunctionDecl *BodyDecl;
+ if (auto *S = FD->getBody(BodyDecl))
+ findGMFReachableDeclExprs(S);
+ return;
+ }
+
+ if (auto TS = dyn_cast<ClassTemplateSpecializationDecl>(Orig)) {
+ markGMFDeclsReachableFrom(TS->getSpecializedTemplate());
+ for (auto B : TS->bases()) {
+ auto *T = B.getType().getTypePtr();
+ assert(T->isRecordType() && "base not a record?");
+ markGMFDeclsReachableFrom(T->getAsCXXRecordDecl());
+ }
+ return;
+ }
+
+ if (auto CD = dyn_cast<ClassTemplateDecl>(Orig)) {
+ markGMFDeclsReachableFrom(CD->getTemplatedDecl());
+ for (auto *SP : CD->specializations())
+ markGMFDeclsReachableFrom(SP);
+ for (auto *TA : *CD->getTemplateParameters())
+ markGMFDeclsReachableFrom(TA);
+ return;
+ }
+
+ if (auto *RD = dyn_cast<CXXRecordDecl>(Orig)) {
+ if (!RD->isCompleteDefinition())
+ return;
+ DeclContext *DC = Decl::castToDeclContext(RD);
+ for (auto *D : DC->decls())
+ markGMFDeclsReachableFrom(D);
+ for (auto B : RD->bases()) {
+ auto *T = B.getType().getTypePtr();
+ assert(T->isRecordType() && "base not a record?");
+ auto *BR = T->getAsCXXRecordDecl();
+ markGMFDeclsReachableFrom(BR);
+ }
+ return;
+ }
+
+ if (auto *TTA = dyn_cast<TemplateTypeParmDecl>(Orig)) {
+ if (TTA->hasDefaultArgument())
+ findGMFReachableDeclsForType(TTA->getDefaultArgument().getTypePtr());
+ return;
+ }
+
+ if (auto *PD = dyn_cast<ParmVarDecl>(Orig)) {
+ findGMFReachableDeclsForType(PD->getOriginalType().getTypePtr());
+ if (auto *E = PD->getDefaultArg())
+ findGMFReachableDeclExprs(E);
+ return;
+ }
+
+ if (auto *VD = dyn_cast<VarDecl>(Orig)) {
+ findGMFReachableDeclsForType(VD->getType().getTypePtr());
+ if (VD->hasInit())
+ findGMFReachableDeclExprs(VD->getInit());
+ return;
+ }
+
+ if (auto *FD = dyn_cast<FieldDecl>(Orig)) {
+ findGMFReachableDeclsForType(FD->getType().getTypePtr());
+ if (auto *Init = FD->getInClassInitializer())
+ findGMFReachableDeclExprs(Init);
+ return;
+ }
+
+ if (auto *FD = dyn_cast<FriendDecl>(Orig)) {
+ if (auto *DD = FD->getFriendDecl())
+ markGMFDeclsReachableFrom(DD, MakeVisible);
+ return;
+ }
+}
Index: clang/lib/Sema/SemaLambda.cpp
===================================================================
--- clang/lib/Sema/SemaLambda.cpp
+++ clang/lib/Sema/SemaLambda.cpp
@@ -874,7 +874,7 @@
NewVD->setReferenced(true);
// FIXME: Pass in a VarDecl::InitializationStyle.
NewVD->setInitStyle(static_cast<VarDecl::InitializationStyle>(InitStyle));
- NewVD->markUsed(Context);
+ markDeclUsed(static_cast<Decl *>(NewVD), Context);
NewVD->setInit(Init);
if (NewVD->isParameterPack())
getCurLambda()->LocalPacks.push_back(NewVD);
@@ -1975,7 +1975,7 @@
Lambda->lookup(
Context.DeclarationNames.getCXXOperatorName(OO_Call)).front());
CallOperator->setReferenced();
- CallOperator->markUsed(Context);
+ markDeclUsed(CallOperator, Context);
ExprResult Init = PerformCopyInitialization(
InitializedEntity::InitializeLambdaToBlock(ConvLocation, Src->getType()),
Index: clang/lib/Sema/SemaInit.cpp
===================================================================
--- clang/lib/Sema/SemaInit.cpp
+++ clang/lib/Sema/SemaInit.cpp
@@ -4692,7 +4692,7 @@
FunctionDecl *Function = Best->Function;
// This is the overload that will be used for this initialization step if we
// use this initialization. Mark it as referenced.
- Function->setReferenced();
+ S.setDeclReferenced(Function);
// Compute the returned type and value kind of the conversion.
QualType cv3T3;
@@ -5363,7 +5363,7 @@
}
FunctionDecl *Function = Best->Function;
- Function->setReferenced();
+ S.setDeclReferenced(Function);
bool HadMultipleCandidates = (CandidateSet.size() > 1);
if (isa<CXXConstructorDecl>(Function)) {
Index: clang/lib/Sema/SemaExpr.cpp
===================================================================
--- clang/lib/Sema/SemaExpr.cpp
+++ clang/lib/Sema/SemaExpr.cpp
@@ -47,6 +47,7 @@
#include "clang/Sema/ParsedTemplate.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/ScopeInfo.h"
+#include "clang/Sema/Sema.h"
#include "clang/Sema/SemaFixItUtils.h"
#include "clang/Sema/SemaInternal.h"
#include "clang/Sema/Template.h"
@@ -15789,7 +15790,7 @@
/// ActOnAddrLabel - Parse the GNU address of label extension: "&&foo".
ExprResult Sema::ActOnAddrLabel(SourceLocation OpLoc, SourceLocation LabLoc,
LabelDecl *TheDecl) {
- TheDecl->markUsed(Context);
+ markDeclUsed(TheDecl, Context);
// Create the AST node. The address of a label always has type 'void*'.
return new (Context) AddrLabelExpr(OpLoc, LabLoc, TheDecl,
Context.getPointerType(Context.VoidTy));
@@ -17874,7 +17875,7 @@
bool MightBeOdrUse) {
assert(Func && "No function?");
- Func->setReferenced();
+ setDeclReferenced(Func);
// Recursive functions aren't really used until they're used from some other
// context.
@@ -18107,7 +18108,7 @@
}
}
- Func->markUsed(Context);
+ markDeclUsed(Func, Context);
}
}
@@ -18178,7 +18179,7 @@
}
}
- Var->markUsed(SemaRef.Context);
+ SemaRef.markDeclUsed(Var, SemaRef.Context);
}
void Sema::MarkCaptureUsedInEnclosingContext(VarDecl *Capture,
@@ -19334,7 +19335,7 @@
assert((!E || isa<DeclRefExpr>(E) || isa<MemberExpr>(E) ||
isa<FunctionParmPackExpr>(E)) &&
"Invalid Expr argument to DoMarkVarDeclReferenced");
- Var->setReferenced();
+ SemaRef.setDeclReferenced(Var);
if (Var->isInvalidDecl())
return;
@@ -19610,7 +19611,7 @@
MarkFunctionReferenced(Loc, FD, MightBeOdrUse);
return;
}
- D->setReferenced();
+ setDeclReferenced(D);
}
namespace {
Index: clang/lib/Sema/SemaDeclCXX.cpp
===================================================================
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -8796,7 +8796,7 @@
return;
}
FD->setBody(Body.get());
- FD->markUsed(Context);
+ markDeclUsed(FD, Context);
}
// The exception specification is needed because we are defining the
@@ -13503,7 +13503,7 @@
? Constructor->getEndLoc()
: Constructor->getLocation();
Constructor->setBody(new (Context) CompoundStmt(Loc));
- Constructor->markUsed(Context);
+ markDeclUsed(Constructor, Context);
if (ASTMutationListener *L = getASTMutationListener()) {
L->CompletedImplicitDefinition(Constructor);
@@ -13682,7 +13682,7 @@
}
Constructor->setBody(new (Context) CompoundStmt(InitLoc));
- Constructor->markUsed(Context);
+ markDeclUsed(Constructor, Context);
if (ASTMutationListener *L = getASTMutationListener()) {
L->CompletedImplicitDefinition(Constructor);
@@ -13793,7 +13793,7 @@
? Destructor->getEndLoc()
: Destructor->getLocation();
Destructor->setBody(new (Context) CompoundStmt(Loc));
- Destructor->markUsed(Context);
+ markDeclUsed(Destructor, Context);
if (ASTMutationListener *L = getASTMutationListener()) {
L->CompletedImplicitDefinition(Destructor);
@@ -14652,7 +14652,7 @@
assert(!Body.isInvalid() && "Compound statement creation cannot fail");
}
CopyAssignOperator->setBody(Body.getAs<Stmt>());
- CopyAssignOperator->markUsed(Context);
+ markDeclUsed(CopyAssignOperator, Context);
if (ASTMutationListener *L = getASTMutationListener()) {
L->CompletedImplicitDefinition(CopyAssignOperator);
@@ -15022,7 +15022,7 @@
assert(!Body.isInvalid() && "Compound statement creation cannot fail");
}
MoveAssignOperator->setBody(Body.getAs<Stmt>());
- MoveAssignOperator->markUsed(Context);
+ markDeclUsed(MoveAssignOperator, Context);
if (ASTMutationListener *L = getASTMutationListener()) {
L->CompletedImplicitDefinition(MoveAssignOperator);
@@ -15166,7 +15166,7 @@
Sema::CompoundScopeRAII CompoundScope(*this);
CopyConstructor->setBody(
ActOnCompoundStmt(Loc, Loc, None, /*isStmtExpr=*/false).getAs<Stmt>());
- CopyConstructor->markUsed(Context);
+ markDeclUsed(CopyConstructor, Context);
}
if (ASTMutationListener *L = getASTMutationListener()) {
@@ -15292,7 +15292,7 @@
Sema::CompoundScopeRAII CompoundScope(*this);
MoveConstructor->setBody(ActOnCompoundStmt(
Loc, Loc, None, /*isStmtExpr=*/ false).getAs<Stmt>());
- MoveConstructor->markUsed(Context);
+ markDeclUsed(MoveConstructor, Context);
}
if (ASTMutationListener *L = getASTMutationListener()) {
@@ -15343,7 +15343,7 @@
// Fill in the __invoke function with a dummy implementation. IR generation
// will fill in the actual details. Update its type in case it contained
// an 'auto'.
- Invoker->markUsed(Context);
+ markDeclUsed(Invoker, Context);
Invoker->setReferenced();
Invoker->setType(Conv->getReturnType()->getPointeeType());
Invoker->setBody(new (Context) CompoundStmt(Conv->getLocation()));
@@ -15355,7 +15355,7 @@
Stmt *Return = BuildReturnStmt(Conv->getLocation(), FunctionRef).get();
Conv->setBody(CompoundStmt::Create(Context, Return, Conv->getLocation(),
Conv->getLocation()));
- Conv->markUsed(Context);
+ markDeclUsed(Conv, Context);
Conv->setReferenced();
if (ASTMutationListener *L = getASTMutationListener()) {
@@ -15410,7 +15410,7 @@
Stmt *ReturnS = Return.get();
Conv->setBody(CompoundStmt::Create(Context, ReturnS, Conv->getLocation(),
Conv->getLocation()));
- Conv->markUsed(Context);
+ markDeclUsed(Conv, Context);
// We're done; notify the mutation listener, if any.
if (ASTMutationListener *L = getASTMutationListener()) {
Index: clang/lib/Sema/SemaDeclAttr.cpp
===================================================================
--- clang/lib/Sema/SemaDeclAttr.cpp
+++ clang/lib/Sema/SemaDeclAttr.cpp
@@ -2012,7 +2012,7 @@
LookupResult LR(S, target, Sema::LookupOrdinaryName);
if (S.LookupQualifiedName(LR, S.getCurLexicalContext()))
for (NamedDecl *ND : LR)
- ND->markUsed(S.Context);
+ S.markDeclUsed(ND, S.Context);
}
D->addAttr(::new (S.Context) AliasAttr(S.Context, AL, Str));
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -4114,7 +4114,7 @@
// Merge "used" flag.
if (Old->getMostRecentDecl()->isUsed(false))
- New->setIsUsed();
+ setDeclIsUsed(New);
// Merge attributes from the parameters. These can mismatch with K&R
// declarations.
@@ -4530,7 +4530,7 @@
// Merge "used" flag.
if (Old->getMostRecentDecl()->isUsed(false))
- New->setIsUsed();
+ setDeclIsUsed(New);
// Keep a chain of previous declarations.
New->setPreviousDecl(Old);
Index: clang/lib/Sema/Sema.cpp
===================================================================
--- clang/lib/Sema/Sema.cpp
+++ clang/lib/Sema/Sema.cpp
@@ -1118,6 +1118,10 @@
assert(DelayedDiagnostics.getCurrentPool() == nullptr
&& "reached end of translation unit with a pool attached?");
+ // We have finished checking if decls in a GMF need to be made reachable.
+ SeenDecls.clear();
+ SeenTypes.clear();
+
// If code completion is enabled, don't perform any end-of-translation-unit
// work.
if (PP.isCodeCompletionEnabled())
Index: clang/lib/AST/TextNodeDumper.cpp
===================================================================
--- clang/lib/AST/TextNodeDumper.cpp
+++ clang/lib/AST/TextNodeDumper.cpp
@@ -12,6 +12,7 @@
#include "clang/AST/TextNodeDumper.h"
#include "clang/AST/APValue.h"
+#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclFriend.h"
#include "clang/AST/DeclOpenMP.h"
#include "clang/AST/DeclTemplate.h"
@@ -57,6 +58,29 @@
llvm_unreachable("Decl that isn't part of DeclNodes.inc!");
}
+static void dumpModuleOwnership(raw_ostream &OS, const Decl *D) {
+ switch (D->getModuleOwnershipKind()) {
+ case Decl::ModuleOwnershipKind::Unowned:
+ // OS << " Unowned";
+ break;
+ case Decl::ModuleOwnershipKind::Visible:
+ OS << " Visible";
+ break;
+ case Decl::ModuleOwnershipKind::VisibleWhenImported:
+ OS << " VisibleWhenImported";
+ break;
+ case Decl::ModuleOwnershipKind::ReachableWhenImported:
+ OS << " ReachableWhenImported";
+ break;
+ case Decl::ModuleOwnershipKind::ModulePrivate:
+ OS << " __module_private__";
+ break;
+ case Decl::ModuleOwnershipKind::ModuleDiscardable:
+ OS << " ModuleDiscardable";
+ break;
+ }
+}
+
TextNodeDumper::TextNodeDumper(raw_ostream &OS, const ASTContext &Context,
bool ShowColors)
: TextTreeStructure(OS, ShowColors), OS(OS), ShowColors(ShowColors),
@@ -1615,8 +1639,7 @@
void TextNodeDumper::VisitTypedefDecl(const TypedefDecl *D) {
dumpName(D);
dumpType(D->getUnderlyingType());
- if (D->isModulePrivate())
- OS << " __module_private__";
+ dumpModuleOwnership(OS, D);
}
void TextNodeDumper::VisitEnumDecl(const EnumDecl *D) {
@@ -1627,8 +1650,7 @@
OS << " struct";
}
dumpName(D);
- if (D->isModulePrivate())
- OS << " __module_private__";
+ dumpModuleOwnership(OS, D);
if (D->isFixed())
dumpType(D->getIntegerType());
}
@@ -1636,8 +1658,7 @@
void TextNodeDumper::VisitRecordDecl(const RecordDecl *D) {
OS << ' ' << D->getKindName();
dumpName(D);
- if (D->isModulePrivate())
- OS << " __module_private__";
+ dumpModuleOwnership(OS, D);
if (D->isCompleteDefinition())
OS << " definition";
}
@@ -1666,8 +1687,7 @@
OS << " inline";
if (D->isVirtualAsWritten())
OS << " virtual";
- if (D->isModulePrivate())
- OS << " __module_private__";
+ dumpModuleOwnership(OS, D);
if (D->isPure())
OS << " pure";
@@ -1744,8 +1764,7 @@
dumpType(D->getType());
if (D->isMutable())
OS << " mutable";
- if (D->isModulePrivate())
- OS << " __module_private__";
+ dumpModuleOwnership(OS, D);
}
void TextNodeDumper::VisitVarDecl(const VarDecl *D) {
@@ -1764,8 +1783,7 @@
OS << " tls_dynamic";
break;
}
- if (D->isModulePrivate())
- OS << " __module_private__";
+ dumpModuleOwnership(OS, D);
if (D->isNRVOVariable())
OS << " nrvo";
if (D->isInline())
@@ -1941,6 +1959,7 @@
{
ColorScope Color(OS, ShowColors, DeclKindNameColor);
OS << "DefinitionData";
+ dumpModuleOwnership(OS, D);
}
#define FLAG(fn, name) \
if (D->fn()) \
@@ -1980,6 +1999,7 @@
FLAG(hasConstexprDefaultConstructor, constexpr);
FLAG(needsImplicitDefaultConstructor, needs_implicit);
FLAG(defaultedDefaultConstructorIsConstexpr, defaulted_is_constexpr);
+ dumpModuleOwnership(OS, D);
});
AddChild([=] {
@@ -1998,6 +2018,7 @@
if (!D->needsOverloadResolutionForCopyConstructor())
FLAG(defaultedCopyConstructorIsDeleted, defaulted_is_deleted);
FLAG(implicitCopyConstructorHasConstParam, implicit_has_const_param);
+ dumpModuleOwnership(OS, D);
});
AddChild([=] {
@@ -2015,6 +2036,7 @@
needs_overload_resolution);
if (!D->needsOverloadResolutionForMoveConstructor())
FLAG(defaultedMoveConstructorIsDeleted, defaulted_is_deleted);
+ dumpModuleOwnership(OS, D);
});
AddChild([=] {
@@ -2030,6 +2052,7 @@
FLAG(needsImplicitCopyAssignment, needs_implicit);
FLAG(needsOverloadResolutionForCopyAssignment, needs_overload_resolution);
FLAG(implicitCopyAssignmentHasConstParam, implicit_has_const_param);
+ dumpModuleOwnership(OS, D);
});
AddChild([=] {
@@ -2044,6 +2067,7 @@
FLAG(hasUserDeclaredMoveAssignment, user_declared);
FLAG(needsImplicitMoveAssignment, needs_implicit);
FLAG(needsOverloadResolutionForMoveAssignment, needs_overload_resolution);
+ dumpModuleOwnership(OS, D);
});
AddChild([=] {
@@ -2061,6 +2085,7 @@
FLAG(needsOverloadResolutionForDestructor, needs_overload_resolution);
if (!D->needsOverloadResolutionForDestructor())
FLAG(defaultedDestructorIsDeleted, defaulted_is_deleted);
+ dumpModuleOwnership(OS, D);
});
});
@@ -2072,20 +2097,24 @@
dumpType(I.getType());
if (I.isPackExpansion())
OS << "...";
+ dumpModuleOwnership(OS, D);
});
}
}
void TextNodeDumper::VisitFunctionTemplateDecl(const FunctionTemplateDecl *D) {
dumpName(D);
+ dumpModuleOwnership(OS, D);
}
void TextNodeDumper::VisitClassTemplateDecl(const ClassTemplateDecl *D) {
dumpName(D);
+ dumpModuleOwnership(OS, D);
}
void TextNodeDumper::VisitVarTemplateDecl(const VarTemplateDecl *D) {
dumpName(D);
+ dumpModuleOwnership(OS, D);
}
void TextNodeDumper::VisitBuiltinTemplateDecl(const BuiltinTemplateDecl *D) {
@@ -2109,6 +2138,7 @@
if (D->isParameterPack())
OS << " ...";
dumpName(D);
+ dumpModuleOwnership(OS, D);
}
void TextNodeDumper::VisitNonTypeTemplateParmDecl(
@@ -2118,6 +2148,7 @@
if (D->isParameterPack())
OS << " ...";
dumpName(D);
+ dumpModuleOwnership(OS, D);
}
void TextNodeDumper::VisitTemplateTemplateParmDecl(
@@ -2126,6 +2157,7 @@
if (D->isParameterPack())
OS << " ...";
dumpName(D);
+ dumpModuleOwnership(OS, D);
}
void TextNodeDumper::VisitUsingDecl(const UsingDecl *D) {
@@ -2133,6 +2165,7 @@
if (D->getQualifier())
D->getQualifier()->print(OS, D->getASTContext().getPrintingPolicy());
OS << D->getDeclName();
+ dumpModuleOwnership(OS, D);
}
void TextNodeDumper::VisitUsingEnumDecl(const UsingEnumDecl *D) {
@@ -2160,6 +2193,7 @@
void TextNodeDumper::VisitUsingShadowDecl(const UsingShadowDecl *D) {
OS << ' ';
dumpBareDeclRef(D->getTargetDecl());
+ dumpModuleOwnership(OS, D);
}
void TextNodeDumper::VisitConstructorUsingShadowDecl(
@@ -2206,6 +2240,7 @@
void TextNodeDumper::VisitFriendDecl(const FriendDecl *D) {
if (TypeSourceInfo *T = D->getFriendType())
dumpType(T->getType());
+ dumpModuleOwnership(OS, D);
}
void TextNodeDumper::VisitObjCIvarDecl(const ObjCIvarDecl *D) {
Index: clang/lib/AST/Decl.cpp
===================================================================
--- clang/lib/AST/Decl.cpp
+++ clang/lib/AST/Decl.cpp
@@ -590,6 +590,7 @@
case Decl::ModuleOwnershipKind::Unowned:
case Decl::ModuleOwnershipKind::ReachableWhenImported:
case Decl::ModuleOwnershipKind::ModulePrivate:
+ case Decl::ModuleOwnershipKind::ModuleDiscardable:
return false;
case Decl::ModuleOwnershipKind::Visible:
case Decl::ModuleOwnershipKind::VisibleWhenImported:
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -2265,7 +2265,52 @@
bool isAcceptableSlow(const NamedDecl *D, AcceptableKind Kind);
+ llvm::SmallPtrSet<const Decl *, 32> SeenDecls;
+ llvm::SmallPtrSet<const clang::Type *, 32> SeenTypes;
+
+ /// Examine a type, unwrapping ay decls that might be in the GMF.
+ void findGMFReachableDeclsForType(const Type *T);
+
+ /// Walk a statement/expression tree looking for cases that require GMF decls
+ /// to be made reachable.
+ void findGMFReachableDeclExprs(clang::Stmt *S);
+
+ /// Starting from a decl D mark any additional decls in the GMF this makes
+ /// reachable. Make the first decl, D, visible if so specified.
+ void markGMFDeclsReachableFrom(Decl *D, bool MakeVisible = false);
+
+ // We have marked some decl as used (or referenced) and therefore (if we are
+ // in a module purview, and there is a GMF) we need to see if that use or
+ // reference requires us to make GMF decl(s) reachable.
+ void handleGMFDeclsReachableFrom(Decl *D) {
+ // Early exit for the cases we do not need to consider.
+ if (!GlobalModuleFragment || !isCurrentModulePurview())
+ return;
+
+ // We only need to make the decls reachable, not visible.
+ markGMFDeclsReachableFrom(D);
+ }
+
public:
+ /// To implement C++20 GMF elision semantics, we initially mark decls in the
+ /// Global Module Fragment as "ModuleDiscardable". If the decl is then used
+ /// within the module purview, we reset that to visible.
+ void setDeclIsUsed(Decl *D) {
+ D->setIsUsed();
+ handleGMFDeclsReachableFrom(D);
+ }
+
+ /// Likewise when the decl is marked used and notifies mutation listeners.
+ void markDeclUsed(Decl *D, ASTContext &C) {
+ D->markUsed(C);
+ handleGMFDeclsReachableFrom(D);
+ }
+
+ void setDeclReferenced(Decl *D, bool R = true) {
+ D->setReferenced(R);
+ handleGMFDeclsReachableFrom(D);
+ }
+
/// Get the module unit whose scope we are currently within.
Module *getCurrentModule() const {
return ModuleScopes.empty() ? nullptr : ModuleScopes.back().Module;
Index: clang/include/clang/AST/DeclBase.h
===================================================================
--- clang/include/clang/AST/DeclBase.h
+++ clang/include/clang/AST/DeclBase.h
@@ -232,9 +232,12 @@
/// This declaration has an owning module, but is only visible to
/// lookups that occur within that module.
- /// The discarded declarations in global module fragment belongs
- /// to this group too.
- ModulePrivate
+ ModulePrivate,
+
+ /// This declaration is part of a Global Module Fragment, it is permitted
+ /// to discard it and therefore it is not reachable or visible to importers
+ /// of the named module of which the GMF is part.
+ ModuleDiscardable
};
protected:
@@ -594,6 +597,12 @@
void setReferenced(bool R = true) { Referenced = R; }
+ /// Whether this declaration should be retained if it is used.
+ bool isModuleDiscardable() const {
+ return getModuleOwnershipKind() ==
+ Decl::ModuleOwnershipKind::ModuleDiscardable;
+ }
+
/// Whether this declaration is a top-level declaration (function,
/// global variable, etc.) that is lexically inside an objc container
/// definition.
@@ -633,9 +642,10 @@
return getModuleOwnershipKind() > ModuleOwnershipKind::VisibleWhenImported;
}
- /// FIXME: Implement discarding declarations actually in global module
- /// fragment. See [module.global.frag]p3,4 for details.
- bool isDiscardedInGlobalModuleFragment() const { return false; }
+ /// See [module.global.frag]p3,4 for details.
+ bool isDiscardedInGlobalModuleFragment() const {
+ return getModuleOwnershipKind() == ModuleOwnershipKind::ModuleDiscardable;
+ }
/// Return true if this declaration has an attribute which acts as
/// definition of the entity, such as 'alias' or 'ifunc'.
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits