From 55ca8f64431df05fb16ee91597acab142f8272f7 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Thu, 4 Jun 2009 00:03:07 +0000 Subject: [PATCH] When performing template argument deduction, ensure that multiple deductions of the same template parameter are equivalent. This allows us to implement the is_same type trait (!). Also, move template argument deduction into its own file and update a few build systems with this change (grrrr). llvm-svn: 72819 --- clang/include/clang/AST/DeclTemplate.h | 17 ++- clang/lib/Sema/CMakeLists.txt | 1 + clang/lib/Sema/Sema.h | 8 -- clang/lib/Sema/SemaTemplate.cpp | 100 +++------------- clang/lib/Sema/SemaTemplateDeduction.cpp | 121 ++++++++++++++++++++ clang/lib/Sema/SemaTemplateInstantiate.cpp | 4 + clang/test/SemaTemplate/temp_class_spec.cpp | 18 +++ 7 files changed, 170 insertions(+), 99 deletions(-) create mode 100644 clang/lib/Sema/SemaTemplateDeduction.cpp diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index 226af3411d55..556ee288a0dc 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -390,20 +390,21 @@ class TemplateArgument { public: /// \brief The type of template argument we're storing. enum ArgKind { + Null = 0, /// The template argument is a type. It's value is stored in the /// TypeOrValue field. - Type = 0, + Type = 1, /// The template argument is a declaration - Declaration = 1, + Declaration = 2, /// The template argument is an integral value stored in an llvm::APSInt. - Integral = 2, + Integral = 3, /// The template argument is a value- or type-dependent expression /// stored in an Expr*. - Expression = 3 + Expression = 4 } Kind; /// \brief Construct an empty, invalid template argument. - TemplateArgument() : TypeOrValue(0), StartLoc(), Kind(Type) { } + TemplateArgument() : TypeOrValue(0), StartLoc(), Kind(Null) { } /// \brief Construct a template type argument. TemplateArgument(SourceLocation Loc, QualType T) : Kind(Type) { @@ -484,6 +485,9 @@ public: /// \brief Return the kind of stored template argument. ArgKind getKind() const { return Kind; } + /// \brief Determine whether this template argument has no value. + bool isNull() const { return Kind == Null; } + /// \brief Retrieve the template argument as a type. QualType getAsType() const { if (Kind != Type) @@ -534,6 +538,9 @@ public: void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(Kind); switch (Kind) { + case Null: + break; + case Type: getAsType().Profile(ID); break; diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index 321dac18b6a2..85c67df8f1de 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -23,6 +23,7 @@ add_clang_library(clangSema SemaOverload.cpp SemaStmt.cpp SemaTemplate.cpp + SemaTemplateDeduction.cpp SemaTemplateInstantiate.cpp SemaTemplateInstantiateDecl.cpp SemaTemplateInstantiateExpr.cpp diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index c428d29367de..3969da8eb9b9 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -2019,14 +2019,6 @@ public: const IdentifierInfo &II, SourceRange Range); - bool DeduceTemplateArguments(QualType Param, QualType Arg, - llvm::SmallVectorImpl &Deduced); - bool DeduceTemplateArguments(const TemplateArgument &Param, - const TemplateArgument &Arg, - llvm::SmallVectorImpl &Deduced); - bool DeduceTemplateArguments(const TemplateArgumentList &ParamList, - const TemplateArgumentList &ArgList, - llvm::SmallVectorImpl &Deduced); bool DeduceTemplateArguments(ClassTemplatePartialSpecializationDecl *Partial, const TemplateArgumentList &TemplateArgs); diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 782a0d87d897..dede3bba7502 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -750,6 +750,10 @@ static void CanonicalizeTemplateArguments(const TemplateArgument *TemplateArgs, Canonical.reserve(NumTemplateArgs); for (unsigned Idx = 0; Idx < NumTemplateArgs; ++Idx) { switch (TemplateArgs[Idx].getKind()) { + case TemplateArgument::Null: + assert(false && "Should never see a NULL template argument here"); + break; + case TemplateArgument::Expression: // FIXME: Build canonical expression (!) Canonical.push_back(TemplateArgs[Idx]); @@ -765,11 +769,13 @@ static void CanonicalizeTemplateArguments(const TemplateArgument *TemplateArgs, Canonical.push_back(TemplateArgument(SourceLocation(), *TemplateArgs[Idx].getAsIntegral(), TemplateArgs[Idx].getIntegralType())); + break; case TemplateArgument::Type: { QualType CanonType = Context.getCanonicalType(TemplateArgs[Idx].getAsType()); Canonical.push_back(TemplateArgument(SourceLocation(), CanonType)); + break; } } } @@ -1092,6 +1098,10 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template, } switch (Arg.getKind()) { + case TemplateArgument::Null: + assert(false && "Should never see a NULL template argument here"); + break; + case TemplateArgument::Expression: { Expr *E = Arg.getAsExpr(); if (CheckTemplateArgument(NTTP, NTTPType, E, &Converted)) @@ -1131,6 +1141,10 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template, = cast(*Param); switch (Arg.getKind()) { + case TemplateArgument::Null: + assert(false && "Should never see a NULL template argument here"); + break; + case TemplateArgument::Expression: { Expr *ArgExpr = Arg.getAsExpr(); if (ArgExpr && isa(ArgExpr) && @@ -2563,89 +2577,3 @@ Sema::CheckTypenameType(NestedNameSpecifier *NNS, const IdentifierInfo &II, << Name; return QualType(); } - -// FIXME: Move to SemaTemplateDeduction.cpp -bool -Sema::DeduceTemplateArguments(QualType Param, QualType Arg, - llvm::SmallVectorImpl &Deduced) { - // We only want to look at the canonical types, since typedefs and - // sugar are not part of template argument deduction. - Param = Context.getCanonicalType(Param); - Arg = Context.getCanonicalType(Arg); - - // If the parameter type is not dependent, just compare the types - // directly. - if (!Param->isDependentType()) - return Param == Arg; - - // FIXME: Use a visitor or switch to handle all of the kinds of - // types that the parameter may be. - if (const TemplateTypeParmType *TemplateTypeParm - = Param->getAsTemplateTypeParmType()) { - (void)TemplateTypeParm; // FIXME: use this - // The argument type can not be less qualified than the parameter - // type. - if (Param.isMoreQualifiedThan(Arg)) - return false; - - unsigned Quals = Arg.getCVRQualifiers() & ~Param.getCVRQualifiers(); - QualType DeducedType = Arg.getQualifiedType(Quals); - // FIXME: actually save the deduced type, and check that this - // deduction is consistent. - return true; - } - - if (Param.getCVRQualifiers() != Arg.getCVRQualifiers()) - return false; - - if (const PointerType *PointerParam = Param->getAsPointerType()) { - const PointerType *PointerArg = Arg->getAsPointerType(); - if (!PointerArg) - return false; - - return DeduceTemplateArguments(PointerParam->getPointeeType(), - PointerArg->getPointeeType(), - Deduced); - } - - // FIXME: Many more cases to go (to go). - return false; -} - -bool -Sema::DeduceTemplateArguments(const TemplateArgument &Param, - const TemplateArgument &Arg, - llvm::SmallVectorImpl &Deduced) { - assert(Param.getKind() == Arg.getKind() && - "Template argument kind mismatch during deduction"); - switch (Param.getKind()) { - case TemplateArgument::Type: - return DeduceTemplateArguments(Param.getAsType(), Arg.getAsType(), - Deduced); - - default: - return false; - } -} - -bool -Sema::DeduceTemplateArguments(const TemplateArgumentList &ParamList, - const TemplateArgumentList &ArgList, - llvm::SmallVectorImpl &Deduced) { - assert(ParamList.size() == ArgList.size()); - for (unsigned I = 0, N = ParamList.size(); I != N; ++I) { - if (!DeduceTemplateArguments(ParamList[I], ArgList[I], Deduced)) - return false; - } - return true; -} - - -bool -Sema::DeduceTemplateArguments(ClassTemplatePartialSpecializationDecl *Partial, - const TemplateArgumentList &TemplateArgs) { - llvm::SmallVector Deduced; - Deduced.resize(Partial->getTemplateParameters()->size()); - return DeduceTemplateArguments(Partial->getTemplateArgs(), TemplateArgs, - Deduced); -} diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp new file mode 100644 index 000000000000..a55dadd932b7 --- /dev/null +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -0,0 +1,121 @@ +//===------- SemaTemplateDeduction.cpp - Template Argument Deduction ------===/ +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +//===----------------------------------------------------------------------===/ +// +// This file implements C++ template argument deduction. +// +//===----------------------------------------------------------------------===/ + +#include "Sema.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" +#include "clang/Parse/DeclSpec.h" +#include "llvm/Support/Compiler.h" +using namespace clang; + +static bool DeduceTemplateArguments(ASTContext &Context, QualType Param, + QualType Arg, + llvm::SmallVectorImpl &Deduced) { + // We only want to look at the canonical types, since typedefs and + // sugar are not part of template argument deduction. + Param = Context.getCanonicalType(Param); + Arg = Context.getCanonicalType(Arg); + + // If the parameter type is not dependent, just compare the types + // directly. + if (!Param->isDependentType()) + return Param == Arg; + + // FIXME: Use a visitor or switch to handle all of the kinds of + // types that the parameter may be. + if (const TemplateTypeParmType *TemplateTypeParm + = Param->getAsTemplateTypeParmType()) { + // The argument type can not be less qualified than the parameter + // type. + if (Param.isMoreQualifiedThan(Arg)) + return false; + + assert(TemplateTypeParm->getDepth() == 0 && "Can't deduce with depth > 0"); + + unsigned Quals = Arg.getCVRQualifiers() & ~Param.getCVRQualifiers(); + QualType DeducedType = Arg.getQualifiedType(Quals); + unsigned Index = TemplateTypeParm->getIndex(); + + if (Deduced[Index].isNull()) + Deduced[Index] = TemplateArgument(SourceLocation(), DeducedType); + else { + // C++ [temp.deduct.type]p2: + // [...] If type deduction cannot be done for any P/A pair, or if for + // any pair the deduction leads to more than one possible set of + // deduced values, or if different pairs yield different deduced + // values, or if any template argument remains neither deduced nor + // explicitly specified, template argument deduction fails. + if (Deduced[Index].getAsType() != DeducedType) + return false; + } + return true; + } + + if (Param.getCVRQualifiers() != Arg.getCVRQualifiers()) + return false; + + if (const PointerType *PointerParam = Param->getAsPointerType()) { + const PointerType *PointerArg = Arg->getAsPointerType(); + if (!PointerArg) + return false; + + return DeduceTemplateArguments(Context, + PointerParam->getPointeeType(), + PointerArg->getPointeeType(), + Deduced); + } + + // FIXME: Many more cases to go (to go). + return false; +} + +static bool +DeduceTemplateArguments(ASTContext &Context, const TemplateArgument &Param, + const TemplateArgument &Arg, + llvm::SmallVectorImpl &Deduced) { + assert(Param.getKind() == Arg.getKind() && + "Template argument kind mismatch during deduction"); + switch (Param.getKind()) { + case TemplateArgument::Type: + return DeduceTemplateArguments(Context, Param.getAsType(), + Arg.getAsType(), Deduced); + + default: + return false; + } +} + +static bool +DeduceTemplateArguments(ASTContext &Context, + const TemplateArgumentList &ParamList, + const TemplateArgumentList &ArgList, + llvm::SmallVectorImpl &Deduced) { + assert(ParamList.size() == ArgList.size()); + for (unsigned I = 0, N = ParamList.size(); I != N; ++I) { + if (!DeduceTemplateArguments(Context, ParamList[I], ArgList[I], Deduced)) + return false; + } + return true; +} + + +bool +Sema::DeduceTemplateArguments(ClassTemplatePartialSpecializationDecl *Partial, + const TemplateArgumentList &TemplateArgs) { + llvm::SmallVector Deduced; + Deduced.resize(Partial->getTemplateParameters()->size()); + return ::DeduceTemplateArguments(Context, Partial->getTemplateArgs(), + TemplateArgs, Deduced); +} diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index d3d771b0ebb6..0400b4c06040 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -503,6 +503,10 @@ InstantiateTemplateSpecializationType( for (TemplateSpecializationType::iterator Arg = T->begin(), ArgEnd = T->end(); Arg != ArgEnd; ++Arg) { switch (Arg->getKind()) { + case TemplateArgument::Null: + assert(false && "Should never have a NULL template argument"); + break; + case TemplateArgument::Type: { QualType T = SemaRef.InstantiateType(Arg->getAsType(), TemplateArgs, diff --git a/clang/test/SemaTemplate/temp_class_spec.cpp b/clang/test/SemaTemplate/temp_class_spec.cpp index df652b5ba38d..1efb364a1b27 100644 --- a/clang/test/SemaTemplate/temp_class_spec.cpp +++ b/clang/test/SemaTemplate/temp_class_spec.cpp @@ -18,3 +18,21 @@ int array0[is_pointer::value? -1 : 1]; int array1[is_pointer::value? 1 : -1]; int array2[is_pointer::value? 1 : -1]; // expected-error{{partial ordering}} \ // expected-error{{negative}} + +template +struct is_same { + static const bool value = false; +}; + +template +struct is_same { + static const bool value = true; +}; + +typedef int INT; +typedef INT* int_ptr; + +int is_same0[is_same::value? 1 : -1]; +int is_same1[is_same::value? 1 : -1]; +int is_same2[is_same::value? -1 : 1]; +int is_same3[is_same::value? -1 : 1];