59 changes: 58 additions & 1 deletion clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "clang/AST/ExprCXX.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/TypeVisitor.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/PartialDiagnostic.h"
#include "clang/Basic/TargetInfo.h"
Expand Down Expand Up @@ -208,7 +209,8 @@ TemplateNameKind Sema::isTemplateName(Scope *S,
R.suppressDiagnostics();
} else {
assert(isa<ClassTemplateDecl>(TD) || isa<TemplateTemplateParmDecl>(TD) ||
isa<TypeAliasTemplateDecl>(TD) || isa<VarTemplateDecl>(TD));
isa<TypeAliasTemplateDecl>(TD) || isa<VarTemplateDecl>(TD) ||
isa<BuiltinTemplateDecl>(TD));
TemplateKind =
isa<VarTemplateDecl>(TD) ? TNK_Var_template : TNK_Type_template;
}
Expand Down Expand Up @@ -2017,6 +2019,58 @@ void Sema::NoteAllFoundTemplates(TemplateName Name) {
}
}

static QualType
checkBuiltinTemplateIdType(Sema &SemaRef, BuiltinTemplateDecl *BTD,
const SmallVectorImpl<TemplateArgument> &Converted,
SourceLocation TemplateLoc,
TemplateArgumentListInfo &TemplateArgs) {
ASTContext &Context = SemaRef.getASTContext();
switch (BTD->getBuiltinTemplateKind()) {
case BTK__make_integer_seq:
// Specializations of __make_integer_seq<S, T, N> are treated like
// S<T, 0, ..., N-1>.

// C++14 [inteseq.intseq]p1:
// T shall be an integer type.
if (!Converted[1].getAsType()->isIntegralType(Context)) {
SemaRef.Diag(TemplateArgs[1].getLocation(),
diag::err_integer_sequence_integral_element_type);
return QualType();
}

// C++14 [inteseq.make]p1:
// If N is negative the program is ill-formed.
TemplateArgument NumArgsArg = Converted[2];
llvm::APSInt NumArgs = NumArgsArg.getAsIntegral();
if (NumArgs < 0) {
SemaRef.Diag(TemplateArgs[2].getLocation(),
diag::err_integer_sequence_negative_length);
return QualType();
}

QualType ArgTy = NumArgsArg.getIntegralType();
TemplateArgumentListInfo SyntheticTemplateArgs;
// The type argument gets reused as the first template argument in the
// synthetic template argument list.
SyntheticTemplateArgs.addArgument(TemplateArgs[1]);
// Expand N into 0 ... N-1.
for (llvm::APSInt I(NumArgs.getBitWidth(), NumArgs.isUnsigned());
I < NumArgs; ++I) {
TemplateArgument TA(Context, I, ArgTy);
Expr *E = SemaRef.BuildExpressionFromIntegralTemplateArgument(
TA, TemplateArgs[2].getLocation())
.getAs<Expr>();
SyntheticTemplateArgs.addArgument(
TemplateArgumentLoc(TemplateArgument(E), E));
}
// The first template argument will be reused as the template decl that
// our synthetic template arguments will be applied to.
return SemaRef.CheckTemplateIdType(Converted[0].getAsTemplate(),
TemplateLoc, SyntheticTemplateArgs);
}
llvm_unreachable("unexpected BuiltinTemplateDecl!");
}

QualType Sema::CheckTemplateIdType(TemplateName Name,
SourceLocation TemplateLoc,
TemplateArgumentListInfo &TemplateArgs) {
Expand Down Expand Up @@ -2171,6 +2225,9 @@ QualType Sema::CheckTemplateIdType(TemplateName Name,
CanonType = Context.getTypeDeclType(Decl);
assert(isa<RecordType>(CanonType) &&
"type of non-dependent specialization is not a RecordType");
} else if (auto *BTD = dyn_cast<BuiltinTemplateDecl>(Template)) {
CanonType = checkBuiltinTemplateIdType(*this, BTD, Converted, TemplateLoc,
TemplateArgs);
}

// Build the fully-sugared type for this class template
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -922,6 +922,11 @@ Decl *TemplateDeclInstantiator::VisitEnumConstantDecl(EnumConstantDecl *D) {
llvm_unreachable("EnumConstantDecls can only occur within EnumDecls.");
}

Decl *
TemplateDeclInstantiator::VisitBuiltinTemplateDecl(BuiltinTemplateDecl *D) {
llvm_unreachable("BuiltinTemplateDecls cannot be instantiated.");
}

Decl *TemplateDeclInstantiator::VisitClassTemplateDecl(ClassTemplateDecl *D) {
bool isFriend = (D->getFriendObjectKind() != Decl::FOK_None);

Expand Down
1 change: 1 addition & 0 deletions clang/lib/Serialization/ASTCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) {
case Decl::ClassScopeFunctionSpecialization:
case Decl::Import:
case Decl::OMPThreadPrivate:
case Decl::BuiltinTemplate:
return false;

// These indirectly derive from Redeclarable<T> but are not actually
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Serialization/ASTReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6412,6 +6412,9 @@ static Decl *getPredefinedDecl(ASTContext &Context, PredefinedDeclIDs ID) {

case PREDEF_DECL_EXTERN_C_CONTEXT_ID:
return Context.getExternCContextDecl();

case PREDEF_DECL_MAKE_INTEGER_SEQ_ID:
return Context.getMakeIntegerSeqDecl();
}
llvm_unreachable("PredefinedDeclIDs unknown enum value");
}
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/Serialization/ASTReaderDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ namespace clang {
DeclID VisitTemplateDecl(TemplateDecl *D);
RedeclarableResult VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D);
void VisitClassTemplateDecl(ClassTemplateDecl *D);
void VisitBuiltinTemplateDecl(BuiltinTemplateDecl *D);
void VisitVarTemplateDecl(VarTemplateDecl *D);
void VisitFunctionTemplateDecl(FunctionTemplateDecl *D);
void VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D);
Expand Down Expand Up @@ -1856,6 +1857,10 @@ void ASTDeclReader::VisitClassTemplateDecl(ClassTemplateDecl *D) {
}
}

void ASTDeclReader::VisitBuiltinTemplateDecl(BuiltinTemplateDecl *D) {
llvm_unreachable("BuiltinTemplates are not serialized");
}

/// TODO: Unify with ClassTemplateDecl version?
/// May require unifying ClassTemplateDecl and
/// VarTemplateDecl beyond TemplateDecl...
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Serialization/ASTWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4136,6 +4136,8 @@ uint64_t ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot,
RegisterPredefDecl(Context.BuiltinMSVaListDecl,
PREDEF_DECL_BUILTIN_MS_VA_LIST_ID);
RegisterPredefDecl(Context.ExternCContext, PREDEF_DECL_EXTERN_C_CONTEXT_ID);
RegisterPredefDecl(Context.MakeIntegerSeqDecl,
PREDEF_DECL_MAKE_INTEGER_SEQ_ID);

// Build a record containing all of the tentative definitions in this file, in
// TentativeDefinitions order. Generally, this record will be empty for
Expand Down
14 changes: 14 additions & 0 deletions clang/test/PCH/make-integer-seq.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// RUN: %clang_cc1 -std=c++14 -x c++-header %s -emit-pch -o %t.pch
// RUN: %clang_cc1 -std=c++14 -x c++ /dev/null -include-pch %t.pch

template <class T, T... I>
struct Seq {
static constexpr T PackSize = sizeof...(I);
};

template <typename T, T N>
using MakeSeq = __make_integer_seq<Seq, T, N>;

void fn1() {
MakeSeq<int, 3> x;
}
47 changes: 47 additions & 0 deletions clang/test/SemaCXX/make_integer_seq.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s

template <class T, T... I>
struct Seq {
static constexpr T PackSize = sizeof...(I);
};

template <typename T, T N>
using MakeSeq = __make_integer_seq<Seq, T, N>;

static_assert(__is_same(MakeSeq<int, 0>, Seq<int>), "");
static_assert(__is_same(MakeSeq<int, 1>, Seq<int, 0>), "");
static_assert(__is_same(MakeSeq<int, 2>, Seq<int, 0, 1>), "");
static_assert(__is_same(MakeSeq<int, 3>, Seq<int, 0, 1, 2>), "");
static_assert(__is_same(MakeSeq<int, 4>, Seq<int, 0, 1, 2, 3>), "");

static_assert(__is_same(MakeSeq<unsigned int, 0U>, Seq<unsigned int>), "");
static_assert(__is_same(MakeSeq<unsigned int, 1U>, Seq<unsigned int, 0U>), "");
static_assert(__is_same(MakeSeq<unsigned int, 2U>, Seq<unsigned int, 0U, 1U>), "");
static_assert(__is_same(MakeSeq<unsigned int, 3U>, Seq<unsigned int, 0U, 1U, 2U>), "");
static_assert(__is_same(MakeSeq<unsigned int, 4U>, Seq<unsigned int, 0U, 1U, 2U, 3U>), "");

static_assert(__is_same(MakeSeq<long long, 0LL>, Seq<long long>), "");
static_assert(__is_same(MakeSeq<long long, 1LL>, Seq<long long, 0LL>), "");
static_assert(__is_same(MakeSeq<long long, 2LL>, Seq<long long, 0LL, 1LL>), "");
static_assert(__is_same(MakeSeq<long long, 3LL>, Seq<long long, 0LL, 1LL, 2LL>), "");
static_assert(__is_same(MakeSeq<long long, 4LL>, Seq<long long, 0LL, 1LL, 2LL, 3LL>), "");

static_assert(__is_same(MakeSeq<unsigned long long, 0ULL>, Seq<unsigned long long>), "");
static_assert(__is_same(MakeSeq<unsigned long long, 1ULL>, Seq<unsigned long long, 0ULL>), "");
static_assert(__is_same(MakeSeq<unsigned long long, 2ULL>, Seq<unsigned long long, 0ULL, 1ULL>), "");
static_assert(__is_same(MakeSeq<unsigned long long, 3ULL>, Seq<unsigned long long, 0ULL, 1ULL, 2ULL>), "");
static_assert(__is_same(MakeSeq<unsigned long long, 4ULL>, Seq<unsigned long long, 0ULL, 1ULL, 2ULL, 3ULL>), "");

template <typename T, T N>
using ErrorSeq = __make_integer_seq<Seq, T, N>; // expected-error{{must have non-negative sequence length}} \
expected-error{{must have integral element type}}

enum Color : int { Red,
Green,
Blue };
using illformed1 = ErrorSeq<Color, Blue>; // expected-note{{in instantiation}}

using illformed2 = ErrorSeq<int, -5>;

template <typename T, T N> void f() {}
__make_integer_seq<f, int, 0> x; // expected-error{{template template parameter must be a class template or type alias template}}
1 change: 1 addition & 0 deletions clang/tools/libclang/CIndex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5110,6 +5110,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) {
case Decl::Import:
case Decl::OMPThreadPrivate:
case Decl::ObjCTypeParam:
case Decl::BuiltinTemplate:
return C;

// Declaration kinds that don't make any sense here, but are
Expand Down