Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -2590,8 +2590,14 @@ def err_auto_non_deduced_not_alone : Error<
def err_implied_std_initializer_list_not_found : Error<
"cannot deduce type of initializer list because std::initializer_list was "
"not found; include <initializer_list>">;
def err_malformed_std_initializer_list : Error<
"std::initializer_list must be a class template with a single type parameter">;
def err_malformed_std_initializer_list
: Error<"std::initializer_list %enum_select<MalformedStdInitializerList>{"
"%TooManyParams{must have exactly one template parameter}|"
"%Constrained{cannot have associated constraints}|"
"%BadParamKind{must have a type template parameter}|"
"%DefaultArg{cannot have default template arguments}|"
"%ParamPack{cannot be a variadic template}|"
"%BadEntityKind{must be a class template}}0">;
def err_auto_init_list_from_c : Error<
"cannot use %select{'auto'|<ERROR>|'__auto_type'}0 with "
"%select{initializer list|array}1 in C">;
Expand Down
51 changes: 41 additions & 10 deletions clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "clang/AST/TypeLoc.h"
#include "clang/AST/TypeOrdering.h"
#include "clang/Basic/AttributeCommonInfo.h"
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Basic/PartialDiagnostic.h"
#include "clang/Basic/Specifiers.h"
#include "clang/Basic/TargetInfo.h"
Expand Down Expand Up @@ -12071,6 +12072,38 @@ NamespaceDecl *Sema::getOrCreateStdNamespace() {
return getStdNamespace();
}

/// Check that the template-head of this class template is acceptable for
/// a declaration of 'std::initializer_list', and optionally diagnose if
/// it is not.
/// \returns true if any issues were found.
static bool CheckStdInitializerList(Sema &S, const ClassTemplateDecl *Template,
bool Diagnose) {
assert(Template && "Template can't be null");
const TemplateParameterList *Params = Template->getTemplateParameters();
int ErrorKind = -1;

if (Params->size() != 1)
ErrorKind = diag::MalformedStdInitializerList::TooManyParams;
else if (Template->hasAssociatedConstraints())
ErrorKind = diag::MalformedStdInitializerList::Constrained;
else {
const auto *Param = dyn_cast<TemplateTypeParmDecl>(Params->getParam(0));
if (!Param)
ErrorKind = diag::MalformedStdInitializerList::BadParamKind;
else if (Param->hasDefaultArgument())
ErrorKind = diag::MalformedStdInitializerList::DefaultArg;
else if (Param->isTemplateParameterPack())
ErrorKind = diag::MalformedStdInitializerList::ParamPack;
else
return false;
}

if (Diagnose)
S.Diag(Template->getLocation(), diag::err_malformed_std_initializer_list)
<< Params->getSourceRange() << ErrorKind;
return true;
}

bool Sema::isStdInitializerList(QualType Ty, QualType *Element) {
assert(getLangOpts().CPlusPlus &&
"Looking for std::initializer_list outside of C++.");
Expand Down Expand Up @@ -12118,10 +12151,7 @@ bool Sema::isStdInitializerList(QualType Ty, QualType *Element) {
return false;
// This is a template called std::initializer_list, but is it the right
// template?
TemplateParameterList *Params = Template->getTemplateParameters();
if (Params->getMinRequiredArguments() != 1)
return false;
if (!isa<TemplateTypeParmDecl>(Params->getParam(0)))
if (CheckStdInitializerList(*this, Template, /*Diagnose=*/false))
return false;

// It's the right template.
Expand All @@ -12137,7 +12167,8 @@ bool Sema::isStdInitializerList(QualType Ty, QualType *Element) {
return true;
}

static ClassTemplateDecl *LookupStdInitializerList(Sema &S, SourceLocation Loc){
static ClassTemplateDecl *LookupStdInitializerList(Sema &S,
SourceLocation Loc) {
NamespaceDecl *Std = S.getStdNamespace();
if (!Std) {
S.Diag(Loc, diag::err_implied_std_initializer_list_not_found);
Expand All @@ -12155,16 +12186,16 @@ static ClassTemplateDecl *LookupStdInitializerList(Sema &S, SourceLocation Loc){
Result.suppressDiagnostics();
// We found something weird. Complain about the first thing we found.
NamedDecl *Found = *Result.begin();
S.Diag(Found->getLocation(), diag::err_malformed_std_initializer_list);
S.Diag(Found->getLocation(), diag::err_malformed_std_initializer_list)
<< diag::MalformedStdInitializerList::BadEntityKind;
S.Diag(Loc, diag::note_used_here);
return nullptr;
Comment on lines +12189 to 12192
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't we use CheckStdInitializerList?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It currently takes a ClassTemplateDecl, but here Found is some other kind of declaration

}

// We found some template called std::initializer_list. Now verify that it's
// correct.
TemplateParameterList *Params = Template->getTemplateParameters();
if (Params->getMinRequiredArguments() != 1 ||
!isa<TemplateTypeParmDecl>(Params->getParam(0))) {
S.Diag(Template->getLocation(), diag::err_malformed_std_initializer_list);
if (CheckStdInitializerList(S, Template, /*Diagnose=*/true)) {
S.Diag(Loc, diag::note_used_here);
return nullptr;
}

Expand Down
77 changes: 77 additions & 0 deletions clang/test/SemaCXX/invalid-std-initializer-list.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// RUN: %clang_cc1 %s -verify=expected,type-param -std=c++23 -DTYPE_PARAM
// RUN: %clang_cc1 %s -verify=expected,others -std=c++23 -DCONSTANT_PARAM
// RUN: %clang_cc1 %s -verify=expected,others -std=c++23 -DTYPE_TEMPLATE_PARAM
// RUN: %clang_cc1 %s -verify=expected,others -std=c++23 -DDEFAULT_ARG
// RUN: %clang_cc1 %s -verify=expected,others -std=c++23 -DMULTIPLE_PARAMS
// RUN: %clang_cc1 %s -verify=expected,others -std=c++23 -DPARAM_PACK
// RUN: %clang_cc1 %s -verify=expected,others -std=c++23 -DCONSTRAINED_PARAM
// RUN: %clang_cc1 %s -verify=expected,others -std=c++23 -DREQUIRES_CLAUSE
// RUN: %clang_cc1 %s -verify=expected,others -std=c++23 -DNONCLASS_TEMPLATE
// RUN: %clang_cc1 %s -verify=expected,others -std=c++23 -DNONTEMPLATE

Comment on lines +1 to +11
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why isn't that a single test?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because otherwise they're redefined

namespace std {

#ifdef TYPE_PARAM
template<class> class initializer_list;
// expected-note@-1 2 {{template is declared here}}
#elifdef CONSTANT_PARAM
template<int> class initializer_list;
// expected-error@-1 2 {{std::initializer_list must have a type template parameter}}
#elifdef TYPE_TEMPLATE_PARAM
template<template<class> class> class initializer_list;
// expected-error@-1 2 {{std::initializer_list must have a type template parameter}}
#elifdef DEFAULT_ARG
template<class = int> class initializer_list;
// expected-error@-1 2 {{std::initializer_list cannot have default template arguments}}
#elifdef MULTIPLE_PARAMS
template<class, class> class initializer_list;
// expected-error@-1 2 {{std::initializer_list must have exactly one template parameter}}
#elifdef PARAM_PACK
template<class...> class initializer_list;
// expected-error@-1 2 {{std::initializer_list cannot be a variadic template}}
#elifdef CONSTRAINED_PARAM
template<class> concept C = true;
template<C> class initializer_list;
// expected-error@-1 2 {{std::initializer_list cannot have associated constraints}}
#elifdef REQUIRES_CLAUSE
template<class> requires true class initializer_list;
// expected-error@-1 2 {{std::initializer_list cannot have associated constraints}}
#elifdef NONCLASS_TEMPLATE
template<class> class IL;
template<class T> using initializer_list = IL<T>;
// expected-error@-1 2 {{std::initializer_list must be a class template}}
#elifdef NONTEMPLATE
class initializer_list;
// expected-error@-1 2 {{std::initializer_list must be a class template}}
#else
#error Unexpected test kind
#endif

}

struct Test { // expected-note 2 {{candidate constructor}}
#ifdef CONSTANT_PARAM
Test(std::initializer_list<1>); // expected-note {{candidate constructor}}
#elifdef TYPE_TEMPLATE_PARAM
template<class> using A = double;
Test(std::initializer_list<A>); // expected-note {{candidate constructor}}
#elifdef MULTIPLE_PARAMS
Test(std::initializer_list<double, double>); // expected-note {{candidate constructor}}
#elifdef NONTEMPLATE
Test(std::initializer_list); // expected-note {{candidate constructor}}
#else
Test(std::initializer_list<double>); // expected-note {{candidate constructor}}
#endif
};
Test test {1.2, 3.4}; // expected-error {{no matching constructor}}

auto x = {1};
// type-param-error@-1 {{implicit instantiation of undefined template}}
// others-note@-2 {{used here}}

void f() {
for(int x : {1, 2});
// type-param-error@-1 {{implicit instantiation of undefined template}}
// type-param-error@-2 {{invalid range expression}}
// others-note@-3 {{used here}}
}