[cfe-commits] r63431 - in /cfe/trunk: include/clang/Basic/DiagnosticSemaKinds.def lib/Parse/ParseInit.cpp lib/Sema/SemaDecl.cpp lib/Sema/SemaInit.cpp lib/Sema/SemaType.cpp test/SemaCXX/dcl_init_aggr.cpp www/cxx_status.html
Douglas Gregor
dgregor at apple.com
Fri Jan 30 14:09:00 PST 2009
Author: dgregor
Date: Fri Jan 30 16:09:00 2009
New Revision: 63431
URL: http://llvm.org/viewvc/llvm-project?rev=63431&view=rev
Log:
Implement and test aggregate initialization in C++. Major changes:
- Support initialization of reference members; complain if any
reference members are left uninitialized.
- Use C++ copy-initialization for initializing each element (falls
back to constraint checking in C)
- Make sure we diagnose when one tries to provide an initializer
list for a non-aggregate.
- Don't complain about empty initializers in C++ (they are permitted)
- Unrelated but necessary: don't bother trying to convert the
decl-specifier-seq to a type when we're dealing with a C++
constructor, destructor, or conversion operator; it results in
spurious warnings.
Added:
cfe/trunk/test/SemaCXX/dcl_init_aggr.cpp
Modified:
cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.def
cfe/trunk/lib/Parse/ParseInit.cpp
cfe/trunk/lib/Sema/SemaDecl.cpp
cfe/trunk/lib/Sema/SemaInit.cpp
cfe/trunk/lib/Sema/SemaType.cpp
cfe/trunk/www/cxx_status.html
Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.def
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.def?rev=63431&r1=63430&r2=63431&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.def (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.def Fri Jan 30 16:09:00 2009
@@ -270,6 +270,10 @@
"declaration of const variable '%0' requires an initializer")
DIAG(err_init_non_aggr_init_list, ERROR,
"initialization of non-aggregate type %0 with an initializer list")
+DIAG(err_init_reference_member_uninitialized, ERROR,
+ "reference member of type %0 uninitialized")
+DIAG(note_uninit_reference_member, NOTE,
+ "uninitialized reference member is here")
// Objective-C++
DIAG(err_objc_decls_may_only_appear_in_global_scope, ERROR,
Modified: cfe/trunk/lib/Parse/ParseInit.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseInit.cpp?rev=63431&r1=63430&r2=63431&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseInit.cpp (original)
+++ cfe/trunk/lib/Parse/ParseInit.cpp Fri Jan 30 16:09:00 2009
@@ -256,10 +256,10 @@
/// was specified for it, if any.
InitListDesignations InitExprDesignations(Actions);
- // We support empty initializers, but tell the user that they aren't using
- // C99-clean code.
if (Tok.is(tok::r_brace)) {
- Diag(LBraceLoc, diag::ext_gnu_empty_initializer);
+ // Empty initializers are a C++ feature and a GNU extension to C.
+ if (!getLang().CPlusPlus)
+ Diag(LBraceLoc, diag::ext_gnu_empty_initializer);
// Match the '}'.
return Actions.ActOnInitList(LBraceLoc, Action::MultiExprArg(Actions),
InitExprDesignations, ConsumeBrace());
Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=63431&r1=63430&r2=63431&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Fri Jan 30 16:09:00 2009
@@ -1074,23 +1074,7 @@
<< Init->getSourceRange();
return CheckSingleInitializer(Init, DeclType, DirectInit);
- } else if (getLangOptions().CPlusPlus) {
- // C++ [dcl.init]p14:
- // [...] If the class is an aggregate (8.5.1), and the initializer
- // is a brace-enclosed list, see 8.5.1.
- //
- // Note: 8.5.1 is handled below; here, we diagnose the case where
- // we have an initializer list and a destination type that is not
- // an aggregate.
- // FIXME: In C++0x, this is yet another form of initialization.
- // FIXME: Move this checking into CheckInitList!
- if (const RecordType *ClassRec = DeclType->getAsRecordType()) {
- const CXXRecordDecl *ClassDecl = cast<CXXRecordDecl>(ClassRec->getDecl());
- if (!ClassDecl->isAggregate())
- return Diag(InitLoc, diag::err_init_non_aggr_init_list)
- << DeclType << Init->getSourceRange();
- }
- }
+ }
bool hadError = CheckInitList(InitList, DeclType);
Init = InitList;
Modified: cfe/trunk/lib/Sema/SemaInit.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaInit.cpp?rev=63431&r1=63430&r2=63431&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaInit.cpp (original)
+++ cfe/trunk/lib/Sema/SemaInit.cpp Fri Jan 30 16:09:00 2009
@@ -69,11 +69,14 @@
unsigned &Index,
InitListExpr *StructuredList,
unsigned &StructuredIndex);
- // FIXME: Does DeclType need to be a reference type?
- void CheckScalarType(InitListExpr *IList, QualType &DeclType,
+ void CheckScalarType(InitListExpr *IList, QualType DeclType,
unsigned &Index,
InitListExpr *StructuredList,
unsigned &StructuredIndex);
+ void CheckReferenceType(InitListExpr *IList, QualType DeclType,
+ unsigned &Index,
+ InitListExpr *StructuredList,
+ unsigned &StructuredIndex);
void CheckVectorType(InitListExpr *IList, QualType DeclType, unsigned &Index,
InitListExpr *StructuredList,
unsigned &StructuredIndex);
@@ -106,6 +109,8 @@
Expr *expr);
int numArrayElements(QualType DeclType);
int numStructUnionElements(QualType DeclType);
+
+ void FillInValueInitializations(InitListExpr *ILE);
public:
InitListChecker(Sema *S, InitListExpr *IL, QualType &T);
bool HadError() { return hadError; }
@@ -116,12 +121,12 @@
};
}
-
/// Recursively replaces NULL values within the given initializer list
/// with expressions that perform value-initialization of the
/// appropriate type.
-static void fillInValueInitializations(ASTContext &Context, InitListExpr *ILE) {
- assert((ILE->getType() != Context.VoidTy) && "Should not have void type");
+void InitListChecker::FillInValueInitializations(InitListExpr *ILE) {
+ assert((ILE->getType() != SemaRef->Context.VoidTy) &&
+ "Should not have void type");
if (const RecordType *RType = ILE->getType()->getAsRecordType()) {
unsigned Init = 0, NumInits = ILE->getNumInits();
for (RecordDecl::field_iterator Field = RType->getDecl()->field_begin(),
@@ -130,17 +135,31 @@
if (Field->isUnnamedBitfield())
continue;
- if (Init >= NumInits)
- break;
-
- // FIXME: Check for fields with reference type in C++?
- if (!ILE->getInit(Init))
+ if (Init >= NumInits) {
+ if (Field->getType()->isReferenceType()) {
+ // C++ [dcl.init.aggr]p9:
+ // If an incomplete or empty initializer-list leaves a
+ // member of reference type uninitialized, the program is
+ // ill-formed.
+ SemaRef->Diag(ILE->getSyntacticForm()->getLocStart(),
+ diag::err_init_reference_member_uninitialized)
+ << Field->getType()
+ << ILE->getSyntacticForm()->getSourceRange();
+ SemaRef->Diag(Field->getLocation(),
+ diag::note_uninit_reference_member);
+ hadError = true;
+ }
+ } else if (!ILE->getInit(Init))
ILE->setInit(Init,
- new (Context) ImplicitValueInitExpr(Field->getType()));
+ new (SemaRef->Context) ImplicitValueInitExpr(Field->getType()));
else if (InitListExpr *InnerILE
= dyn_cast<InitListExpr>(ILE->getInit(Init)))
- fillInValueInitializations(Context, InnerILE);
+ FillInValueInitializations(InnerILE);
++Init;
+
+ // Only look at the first initialization of a union.
+ if (RType->getDecl()->isUnion())
+ break;
}
return;
@@ -148,7 +167,7 @@
QualType ElementType;
- if (const ArrayType *AType = Context.getAsArrayType(ILE->getType()))
+ if (const ArrayType *AType = SemaRef->Context.getAsArrayType(ILE->getType()))
ElementType = AType->getElementType();
else if (const VectorType *VType = ILE->getType()->getAsVectorType())
ElementType = VType->getElementType();
@@ -158,9 +177,10 @@
for (unsigned Init = 0, NumInits = ILE->getNumInits(); Init != NumInits;
++Init) {
if (!ILE->getInit(Init))
- ILE->setInit(Init, new (Context) ImplicitValueInitExpr(ElementType));
+ ILE->setInit(Init,
+ new (SemaRef->Context) ImplicitValueInitExpr(ElementType));
else if (InitListExpr *InnerILE =dyn_cast<InitListExpr>(ILE->getInit(Init)))
- fillInValueInitializations(Context, InnerILE);
+ FillInValueInitializations(InnerILE);
}
}
@@ -175,9 +195,8 @@
= getStructuredSubobjectInit(IL, newIndex, T, 0, 0, SourceRange());
CheckExplicitInitList(IL, T, newIndex, FullyStructuredList, newStructuredIndex);
- if (!hadError) {
- fillInValueInitializations(SemaRef->Context, FullyStructuredList);
- }
+ if (!hadError)
+ FillInValueInitializations(FullyStructuredList);
}
int InitListChecker::numArrayElements(QualType DeclType) {
@@ -219,7 +238,6 @@
else
assert(0 && "CheckImplicitInitList(): Illegal type");
- // FIXME: Perhaps we should move this warning elsewhere?
if (maxElements == 0) {
SemaRef->Diag(ParentIList->getInit(Index)->getLocStart(),
diag::err_implicit_empty_initializer);
@@ -308,6 +326,20 @@
SemaRef->Diag(IList->getLocStart(), diag::err_illegal_initializer_type)
<< DeclType;
hadError = true;
+ } else if (DeclType->isRecordType()) {
+ // C++ [dcl.init]p14:
+ // [...] If the class is an aggregate (8.5.1), and the initializer
+ // is a brace-enclosed list, see 8.5.1.
+ //
+ // Note: 8.5.1 is handled below; here, we diagnose the case where
+ // we have an initializer list and a destination type that is not
+ // an aggregate.
+ // FIXME: In C++0x, this is yet another form of initialization.
+ SemaRef->Diag(IList->getLocStart(), diag::err_init_non_aggr_init_list)
+ << DeclType << IList->getSourceRange();
+ hadError = true;
+ } else if (DeclType->isReferenceType()) {
+ CheckReferenceType(IList, DeclType, Index, StructuredList, StructuredIndex);
} else {
// In C, all types are either scalars or aggregates, but
// additional handling is needed here for C++ (and possibly others?).
@@ -339,21 +371,70 @@
++Index;
} else if (ElemType->isScalarType()) {
CheckScalarType(IList, ElemType, Index, StructuredList, StructuredIndex);
- } else if (expr->getType()->getAsRecordType() &&
- SemaRef->Context.typesAreCompatible(
- expr->getType().getUnqualifiedType(),
- ElemType.getUnqualifiedType())) {
- UpdateStructuredListElement(StructuredList, StructuredIndex, expr);
- ++Index;
- // FIXME: Add checking
+ } else if (ElemType->isReferenceType()) {
+ CheckReferenceType(IList, ElemType, Index, StructuredList, StructuredIndex);
} else {
- CheckImplicitInitList(IList, ElemType, Index, StructuredList,
- StructuredIndex);
- ++StructuredIndex;
- }
+ if (SemaRef->getLangOptions().CPlusPlus) {
+ // C++ [dcl.init.aggr]p12:
+ // All implicit type conversions (clause 4) are considered when
+ // initializing the aggregate member with an ini- tializer from
+ // an initializer-list. If the initializer can initialize a
+ // member, the member is initialized. [...]
+ ImplicitConversionSequence ICS
+ = SemaRef->TryCopyInitialization(expr, ElemType);
+ if (ICS.ConversionKind != ImplicitConversionSequence::BadConversion) {
+ if (SemaRef->PerformImplicitConversion(expr, ElemType, ICS,
+ "initializing"))
+ hadError = true;
+ UpdateStructuredListElement(StructuredList, StructuredIndex, expr);
+ ++Index;
+ return;
+ }
+
+ // Fall through for subaggregate initialization
+ } else {
+ // C99 6.7.8p13:
+ //
+ // The initializer for a structure or union object that has
+ // automatic storage duration shall be either an initializer
+ // list as described below, or a single expression that has
+ // compatible structure or union type. In the latter case, the
+ // initial value of the object, including unnamed members, is
+ // that of the expression.
+ QualType ExprType = SemaRef->Context.getCanonicalType(expr->getType());
+ QualType ElemTypeCanon = SemaRef->Context.getCanonicalType(ElemType);
+ if (SemaRef->Context.typesAreCompatible(ExprType.getUnqualifiedType(),
+ ElemTypeCanon.getUnqualifiedType())) {
+ UpdateStructuredListElement(StructuredList, StructuredIndex, expr);
+ ++Index;
+ return;
+ }
+
+ // Fall through for subaggregate initialization
+ }
+
+ // C++ [dcl.init.aggr]p12:
+ //
+ // [...] Otherwise, if the member is itself a non-empty
+ // subaggregate, brace elision is assumed and the initializer is
+ // considered for the initialization of the first member of
+ // the subaggregate.
+ if (ElemType->isAggregateType() || ElemType->isVectorType()) {
+ CheckImplicitInitList(IList, ElemType, Index, StructuredList,
+ StructuredIndex);
+ ++StructuredIndex;
+ } else {
+ // We cannot initialize this element, so let
+ // PerformCopyInitialization produce the appropriate diagnostic.
+ SemaRef->PerformCopyInitialization(expr, ElemType, "initializing");
+ hadError = true;
+ ++Index;
+ ++StructuredIndex;
+ }
+ }
}
-void InitListChecker::CheckScalarType(InitListExpr *IList, QualType &DeclType,
+void InitListChecker::CheckScalarType(InitListExpr *IList, QualType DeclType,
unsigned &Index,
InitListExpr *StructuredList,
unsigned &StructuredIndex) {
@@ -399,6 +480,49 @@
}
}
+void InitListChecker::CheckReferenceType(InitListExpr *IList, QualType DeclType,
+ unsigned &Index,
+ InitListExpr *StructuredList,
+ unsigned &StructuredIndex) {
+ if (Index < IList->getNumInits()) {
+ Expr *expr = IList->getInit(Index);
+ if (isa<InitListExpr>(expr)) {
+ SemaRef->Diag(IList->getLocStart(), diag::err_init_non_aggr_init_list)
+ << DeclType << IList->getSourceRange();
+ hadError = true;
+ ++Index;
+ ++StructuredIndex;
+ return;
+ }
+
+ Expr *savExpr = expr; // Might be promoted by CheckSingleInitializer.
+ if (SemaRef->CheckReferenceInit(expr, DeclType))
+ hadError = true;
+ else if (savExpr != expr) {
+ // The type was promoted, update initializer list.
+ IList->setInit(Index, expr);
+ }
+ if (hadError)
+ ++StructuredIndex;
+ else
+ UpdateStructuredListElement(StructuredList, StructuredIndex, expr);
+ ++Index;
+ } else {
+ // FIXME: It would be wonderful if we could point at the actual
+ // member. In general, it would be useful to pass location
+ // information down the stack, so that we know the location (or
+ // decl) of the "current object" being initialized.
+ SemaRef->Diag(IList->getLocStart(),
+ diag::err_init_reference_member_uninitialized)
+ << DeclType
+ << IList->getSourceRange();
+ hadError = true;
+ ++Index;
+ ++StructuredIndex;
+ return;
+ }
+}
+
void InitListChecker::CheckVectorType(InitListExpr *IList, QualType DeclType,
unsigned &Index,
InitListExpr *StructuredList,
Modified: cfe/trunk/lib/Sema/SemaType.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaType.cpp?rev=63431&r1=63430&r2=63431&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaType.cpp (original)
+++ cfe/trunk/lib/Sema/SemaType.cpp Fri Jan 30 16:09:00 2009
@@ -18,8 +18,11 @@
#include "clang/Parse/DeclSpec.h"
using namespace clang;
-/// ConvertDeclSpecToType - Convert the specified declspec to the appropriate
-/// type object. This returns null on error.
+/// \brief Convert the specified declspec to the appropriate type
+/// object.
+/// \param DS the declaration specifiers
+/// \returns The type described by the declaration specifiers, or NULL
+/// if there was an error.
QualType Sema::ConvertDeclSpecToType(const DeclSpec &DS) {
// FIXME: Should move the logic from DeclSpec::Finish to here for validity
// checking.
@@ -81,6 +84,7 @@
// "At least one type specifier shall be given in the declaration
// specifiers in each declaration, and in the specifier-qualifier list in
// each struct declaration and type name."
+ // FIXME: this should be a hard error in C++
if (!DS.hasTypeSpecifier())
Diag(DS.getSourceRange().getBegin(), diag::ext_missing_type_specifier);
}
@@ -254,8 +258,26 @@
if (!getLangOptions().C99 && !getLangOptions().CPlusPlus0x &&
D.getDeclSpec().getTypeSpecWidth() == DeclSpec::TSW_longlong)
Diag(D.getDeclSpec().getTypeSpecWidthLoc(), diag::ext_longlong);
-
- QualType T = ConvertDeclSpecToType(D.getDeclSpec());
+
+ // Determine the type of the declarator. Not all forms of declarator
+ // have a type.
+ QualType T;
+ switch (D.getKind()) {
+ case Declarator::DK_Abstract:
+ case Declarator::DK_Normal:
+ case Declarator::DK_Operator:
+ T = ConvertDeclSpecToType(D.getDeclSpec());
+ break;
+
+ case Declarator::DK_Constructor:
+ case Declarator::DK_Destructor:
+ case Declarator::DK_Conversion:
+ // Constructors and destructors don't have return types. Use
+ // "void" instead. Conversion operators will check their return
+ // types separately.
+ T = Context.VoidTy;
+ break;
+ }
// Walk the DeclTypeInfo, building the recursive type as we go. DeclTypeInfos
// are ordered from the identifier out, which is opposite of what we want :).
Added: cfe/trunk/test/SemaCXX/dcl_init_aggr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/dcl_init_aggr.cpp?rev=63431&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/dcl_init_aggr.cpp (added)
+++ cfe/trunk/test/SemaCXX/dcl_init_aggr.cpp Fri Jan 30 16:09:00 2009
@@ -0,0 +1,107 @@
+// RUN: clang -fsyntax-only -pedantic -verify %s
+// C++ [dcl.init.aggr]p2
+struct A {
+ int x;
+ struct B {
+ int i;
+ int j;
+ } b;
+} a1 = { 1, { 2, 3 } };
+
+struct NonAggregate {
+ NonAggregate();
+
+ int a, b;
+};
+NonAggregate non_aggregate_test = { 1, 2 }; // expected-error{{initialization of non-aggregate type 'struct NonAggregate' with an initializer list}}
+
+NonAggregate non_aggregate_test2[2] = { { 1, 2 }, { 3, 4 } }; // expected-error{{initialization of non-aggregate type 'struct NonAggregate' with an initializer list}}
+
+
+// C++ [dcl.init.aggr]p3
+A a_init = A();
+
+// C++ [dcl.init.aggr]p4
+int x[] = { 1, 3, 5 };
+int x_sizecheck[(sizeof(x) / sizeof(int)) == 3? 1 : -1];
+int x2[] = { }; // expected-warning{{zero size arrays are an extension}}
+
+// C++ [dcl.init.aggr]p5
+struct StaticMemberTest {
+ int i;
+ static int s;
+ int *j;
+} smt = { 1, &smt.i };
+
+// C++ [dcl.init.aggr]p6
+char cv[4] = { 'a', 's', 'd', 'f', 0 }; // expected-warning{{excess elements in array initializer}}
+
+// C++ [dcl.init.aggr]p7
+struct TooFew { int a; char* b; int c; };
+TooFew too_few = { 1, "asdf" }; // okay
+
+// C++ [dcl.init.aggr]p8
+struct Empty { };
+struct EmptyTest {
+ Empty s;
+ int i;
+} empty_test = { { }, 3 };
+
+EmptyTest empty_test2 = { 3 }; // expected-error{{initializer for aggregate with no elements requires explicit braces}}
+
+struct NonEmpty {
+ int a;
+ Empty empty;
+};
+struct NonEmptyTest {
+ NonEmpty a, b;
+} non_empty_test = { { }, { } };
+
+// C++ [dcl.init.aggr]p9
+struct HasReference {
+ int i;
+ int &j; // expected-note{{uninitialized reference member is here}}
+};
+int global_int;
+HasReference r1 = { 1, global_int };
+HasReference r2 = { 1 } ; // expected-error{{initialization leaves reference member of type 'int &' uninitialized}}
+
+// C++ [dcl.init.aggr]p10
+// Note: the behavior here is identical to C
+int xs[2][2] = { 3, 1, 4, 2 };
+float y[4][3] = { { 1 }, { 2 }, { 3 }, { 4 } };
+
+// C++ [dcl.init.aggr]p11
+// Note: the behavior here is identical to C
+float y2[4][3] = { { 1, 3, 5 }, { 2, 4, 6 }, { 3, 5, 7 } };
+float same_as_y2[4][3] = { 1, 3, 5, 2, 4, 6, 3, 5, 7 };
+
+// C++ [dcl.init.aggr]p12
+struct A2 {
+ int i;
+ operator int *();
+};
+struct B2 {
+ A2 a1, a2;
+ int *z;
+};
+struct C2 {
+ operator A2();
+};
+struct D2 {
+ operator int();
+};
+A2 a2;
+C2 c2;
+D2 d2;
+B2 b2 = { 4, a2, a2 };
+B2 b2_2 = { 4, d2, 0 };
+// FIXME: B2 b2_3 = { c2, a2, a2 };
+
+// C++ [dcl.init.aggr]p15:
+union u { int a; char* b; };
+u u1 = { 1 };
+u u2 = u1;
+u u3 = 1; // expected-error{{cannot initialize 'u3' with an rvalue of type 'int'}}
+u u4 = { 0, "asdf" }; // expected-warning{{excess elements in array initializer}}
+u u5 = { "asdf" }; // expected-error{{incompatible type initializing 'char const [5]', expected 'int'}}
Modified: cfe/trunk/www/cxx_status.html
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/www/cxx_status.html?rev=63431&r1=63430&r2=63431&view=diff
==============================================================================
--- cfe/trunk/www/cxx_status.html (original)
+++ cfe/trunk/www/cxx_status.html Fri Jan 30 16:09:00 2009
@@ -999,7 +999,14 @@
</tr>
<tr><td> 8.4 [dcl.fct.def]</td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td> 8.5 [dcl.init]</td><td></td><td></td><td></td><td></td><td></td></tr>
-<tr><td> 8.5.1 [dcl.init.aggr]</td><td></td><td></td><td></td><td></td><td></td></tr>
+<tr>
+ <td> 8.5.1[dcl.init.aggr]</td>
+ <td class="advanced" align="center"></td>
+ <td class="advanced" align="center"></td>
+ <td class="medium" align="center"></td>
+ <td class="basic" align="center"></td>
+ <td>No CodeGen for initializing non-aggregates or dynamic initialization.</td>
+</tr>
<tr><td> 8.5.2 [dcl.init.string]</td><td></td><td></td><td></td><td></td><td></td></tr>
<tr>
<td> 8.5.3 [dcl.init.ref]</td>
More information about the cfe-commits
mailing list