Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,8 @@ Bug Fixes to C++ Support
- Clang now issues an error when placement new is used to modify a const-qualified variable
in a ``constexpr`` function. (#GH131432)
- Clang now emits a warning when class template argument deduction for alias templates is used in C++17. (#GH133806)
- No longer crashes when instantiating invalid variable template specialization
whose type depends on itself. (#GH51347), (#GH55872)

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -2956,6 +2956,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
TemplateTemplateParmDecl *insertCanonicalTemplateTemplateParmDeclInternal(
TemplateTemplateParmDecl *CanonTTP) const;

/// Determine whether the given template arguments \p Arg1 and \p Arg2 are
/// equivalent.
bool isSameTemplateArgument(const TemplateArgument &Arg1,
const TemplateArgument &Arg2) const;

/// Type Query functions. If the type is an instance of the specified class,
/// return the Type pointer for the underlying maximally pretty type. This
/// is a member of ASTContext because this may need to do some amount of
Expand Down
14 changes: 11 additions & 3 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -2526,9 +2526,14 @@ def note_implicit_deduction_guide : Note<"implicit deduction guide declared as '
def warn_cxx98_compat_auto_type_specifier : Warning<
"'auto' type specifier is incompatible with C++98">,
InGroup<CXX98Compat>, DefaultIgnore;
def err_auto_variable_cannot_appear_in_own_initializer : Error<
"variable %0 declared with deduced type %1 "
"cannot appear in its own initializer">;
def err_auto_variable_cannot_appear_in_own_initializer
: Error<
"%enum_select<ParsingInitFor>{%Var{variable}|"
"%VarTemplate{variable template}|"
"%VarTemplatePartialSpec{variable template partial specialization}|"
"%VarTemplateExplicitSpec{variable template explicit "
"specialization}}0 %1 "
"declared with deduced type %2 cannot appear in its own initializer">;
def err_binding_cannot_appear_in_own_initializer : Error<
"binding %0 cannot appear in the initializer of its own "
"decomposition declaration">;
Expand Down Expand Up @@ -5299,6 +5304,9 @@ def err_template_member_noparams : Error<
"extraneous 'template<>' in declaration of member %0">;
def err_template_tag_noparams : Error<
"extraneous 'template<>' in declaration of %0 %1">;
def err_var_template_spec_type_depends_on_self : Error<
"the type of variable template specialization %0 declared with deduced type "
"%1 depends on itself">;

def warn_unqualified_call_to_std_cast_function : Warning<
"unqualified call to '%0'">, InGroup<DiagGroup<"unqualified-std-cast-call">>;
Expand Down
49 changes: 49 additions & 0 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7641,6 +7641,55 @@ ASTContext::getCanonicalTemplateArgument(const TemplateArgument &Arg) const {
llvm_unreachable("Unhandled template argument kind");
}

bool ASTContext::isSameTemplateArgument(const TemplateArgument &Arg1,
const TemplateArgument &Arg2) const {
if (Arg1.getKind() != Arg2.getKind())
return false;

switch (Arg1.getKind()) {
case TemplateArgument::Null:
llvm_unreachable("Comparing NULL template argument");

case TemplateArgument::Type:
return hasSameType(Arg1.getAsType(), Arg2.getAsType());

case TemplateArgument::Declaration:
return Arg1.getAsDecl()->getUnderlyingDecl()->getCanonicalDecl() ==
Arg2.getAsDecl()->getUnderlyingDecl()->getCanonicalDecl();

case TemplateArgument::NullPtr:
return hasSameType(Arg1.getNullPtrType(), Arg2.getNullPtrType());

case TemplateArgument::Template:
case TemplateArgument::TemplateExpansion:
return getCanonicalTemplateName(Arg1.getAsTemplateOrTemplatePattern()) ==
getCanonicalTemplateName(Arg2.getAsTemplateOrTemplatePattern());

case TemplateArgument::Integral:
return llvm::APSInt::isSameValue(Arg1.getAsIntegral(),
Arg2.getAsIntegral());

case TemplateArgument::StructuralValue:
return Arg1.structurallyEquals(Arg2);

case TemplateArgument::Expression: {
llvm::FoldingSetNodeID ID1, ID2;
Arg1.getAsExpr()->Profile(ID1, *this, /*Canonical=*/true);
Arg2.getAsExpr()->Profile(ID2, *this, /*Canonical=*/true);
return ID1 == ID2;
}

case TemplateArgument::Pack:
return llvm::equal(
Arg1.getPackAsArray(), Arg2.getPackAsArray(),
[&](const TemplateArgument &Arg1, const TemplateArgument &Arg2) {
return isSameTemplateArgument(Arg1, Arg2);
});
}

llvm_unreachable("Unhandled template argument kind");
}

NestedNameSpecifier *
ASTContext::getCanonicalNestedNameSpecifier(NestedNameSpecifier *NNS) const {
if (!NNS)
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,8 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs,
<< D->getDeclName();
} else {
Diag(Loc, diag::err_auto_variable_cannot_appear_in_own_initializer)
<< D->getDeclName() << cast<VarDecl>(D)->getType();
<< diag::ParsingInitFor::Var << D->getDeclName()
<< cast<VarDecl>(D)->getType();
}
return true;
}
Expand Down
51 changes: 50 additions & 1 deletion clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4371,15 +4371,64 @@ Sema::CheckVarTemplateId(VarTemplateDecl *Template, SourceLocation TemplateLoc,
// Produce a placeholder value if the specialization is dependent.
if (Template->getDeclContext()->isDependentContext() ||
TemplateSpecializationType::anyDependentTemplateArguments(
TemplateArgs, CTAI.CanonicalConverted))
TemplateArgs, CTAI.CanonicalConverted)) {
if (ParsingInitForAutoVars.empty())
return DeclResult();

auto IsSameTemplateArg = [&](const TemplateArgument &Arg1,
const TemplateArgument &Arg2) {
return Context.isSameTemplateArgument(Arg1, Arg2);
};

if (VarDecl *Var = Template->getTemplatedDecl();
ParsingInitForAutoVars.count(Var) &&
llvm::equal(
CTAI.CanonicalConverted,
Template->getTemplateParameters()->getInjectedTemplateArgs(Context),
IsSameTemplateArg)) {
Diag(TemplateNameLoc,
diag::err_auto_variable_cannot_appear_in_own_initializer)
<< diag::ParsingInitFor::VarTemplate << Var << Var->getType();
return true;
}

SmallVector<VarTemplatePartialSpecializationDecl *, 4> PartialSpecs;
Template->getPartialSpecializations(PartialSpecs);
for (VarTemplatePartialSpecializationDecl *Partial : PartialSpecs)
if (ParsingInitForAutoVars.count(Partial) &&
llvm::equal(CTAI.CanonicalConverted,
Partial->getTemplateArgs().asArray(),
IsSameTemplateArg)) {
Diag(TemplateNameLoc,
diag::err_auto_variable_cannot_appear_in_own_initializer)
<< diag::ParsingInitFor::VarTemplatePartialSpec << Partial
<< Partial->getType();
return true;
}

return DeclResult();
}

// Find the variable template specialization declaration that
// corresponds to these arguments.
void *InsertPos = nullptr;
if (VarTemplateSpecializationDecl *Spec =
Template->findSpecialization(CTAI.CanonicalConverted, InsertPos)) {
checkSpecializationReachability(TemplateNameLoc, Spec);
if (Spec->getType()->isUndeducedType()) {
if (ParsingInitForAutoVars.count(Spec))
Diag(TemplateNameLoc,
diag::err_auto_variable_cannot_appear_in_own_initializer)
<< diag::ParsingInitFor::VarTemplateExplicitSpec << Spec
<< Spec->getType();
else
// We are substituting the initializer of this variable template
// specialization.
Diag(TemplateNameLoc, diag::err_var_template_spec_type_depends_on_self)
<< Spec << Spec->getType();

return true;
}
// If we already have a variable template specialization, return it.
return Spec;
}
Expand Down
83 changes: 3 additions & 80 deletions clang/lib/Sema/SemaTemplateDeduction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,27 +114,6 @@ namespace clang {
using namespace clang;
using namespace sema;

/// Compare two APSInts, extending and switching the sign as
/// necessary to compare their values regardless of underlying type.
static bool hasSameExtendedValue(llvm::APSInt X, llvm::APSInt Y) {
if (Y.getBitWidth() > X.getBitWidth())
X = X.extend(Y.getBitWidth());
else if (Y.getBitWidth() < X.getBitWidth())
Y = Y.extend(X.getBitWidth());

// If there is a signedness mismatch, correct it.
if (X.isSigned() != Y.isSigned()) {
// If the signed value is negative, then the values cannot be the same.
if ((Y.isSigned() && Y.isNegative()) || (X.isSigned() && X.isNegative()))
return false;

Y.setIsSigned(true);
X.setIsSigned(true);
}

return X == Y;
}

/// The kind of PartialOrdering we're performing template argument deduction
/// for (C++11 [temp.deduct.partial]).
enum class PartialOrderingKind { None, NonCall, Call };
Expand Down Expand Up @@ -273,7 +252,7 @@ checkDeducedTemplateArguments(ASTContext &Context,
if (Y.getKind() == TemplateArgument::Expression ||
Y.getKind() == TemplateArgument::Declaration ||
(Y.getKind() == TemplateArgument::Integral &&
hasSameExtendedValue(X.getAsIntegral(), Y.getAsIntegral())))
llvm::APSInt::isSameValue(X.getAsIntegral(), Y.getAsIntegral())))
return X.wasDeducedFromArrayBound() ? Y : X;

// All other combinations are incompatible.
Expand Down Expand Up @@ -2574,7 +2553,7 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,

case TemplateArgument::Integral:
if (A.getKind() == TemplateArgument::Integral) {
if (hasSameExtendedValue(P.getAsIntegral(), A.getAsIntegral()))
if (llvm::APSInt::isSameValue(P.getAsIntegral(), A.getAsIntegral()))
return TemplateDeductionResult::Success;
}
Info.FirstArg = P;
Expand Down Expand Up @@ -2828,62 +2807,6 @@ TemplateDeductionResult Sema::DeduceTemplateArguments(
/*HasDeducedAnyParam=*/nullptr);
}

/// Determine whether two template arguments are the same.
static bool isSameTemplateArg(ASTContext &Context, const TemplateArgument &X,
const TemplateArgument &Y) {
if (X.getKind() != Y.getKind())
return false;

switch (X.getKind()) {
case TemplateArgument::Null:
llvm_unreachable("Comparing NULL template argument");

case TemplateArgument::Type:
return Context.getCanonicalType(X.getAsType()) ==
Context.getCanonicalType(Y.getAsType());

case TemplateArgument::Declaration:
return isSameDeclaration(X.getAsDecl(), Y.getAsDecl());

case TemplateArgument::NullPtr:
return Context.hasSameType(X.getNullPtrType(), Y.getNullPtrType());

case TemplateArgument::Template:
case TemplateArgument::TemplateExpansion:
return Context.getCanonicalTemplateName(
X.getAsTemplateOrTemplatePattern()).getAsVoidPointer() ==
Context.getCanonicalTemplateName(
Y.getAsTemplateOrTemplatePattern()).getAsVoidPointer();

case TemplateArgument::Integral:
return hasSameExtendedValue(X.getAsIntegral(), Y.getAsIntegral());

case TemplateArgument::StructuralValue:
return X.structurallyEquals(Y);

case TemplateArgument::Expression: {
llvm::FoldingSetNodeID XID, YID;
X.getAsExpr()->Profile(XID, Context, true);
Y.getAsExpr()->Profile(YID, Context, true);
return XID == YID;
}

case TemplateArgument::Pack: {
unsigned PackIterationSize = X.pack_size();
if (X.pack_size() != Y.pack_size())
return false;
ArrayRef<TemplateArgument> XP = X.pack_elements();
ArrayRef<TemplateArgument> YP = Y.pack_elements();
for (unsigned i = 0; i < PackIterationSize; ++i)
if (!isSameTemplateArg(Context, XP[i], YP[i]))
return false;
return true;
}
}

llvm_unreachable("Invalid TemplateArgument Kind!");
}

TemplateArgumentLoc
Sema::getTrivialTemplateArgumentLoc(const TemplateArgument &Arg,
QualType NTTPType, SourceLocation Loc,
Expand Down Expand Up @@ -3349,7 +3272,7 @@ static TemplateDeductionResult FinishTemplateArgumentDeduction(
break;
TemplateArgument PP = P.isPackExpansion() ? P.getPackExpansionPattern() : P,
PA = A.isPackExpansion() ? A.getPackExpansionPattern() : A;
if (!isSameTemplateArg(S.Context, PP, PA)) {
if (!S.Context.isSameTemplateArgument(PP, PA)) {
if (!P.isPackExpansion() && !A.isPackExpansion()) {
Info.Param = makeTemplateParameter(TPL->getParam(
(AsStack.empty() ? As.end() : AsStack.back().begin()) -
Expand Down
20 changes: 20 additions & 0 deletions clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,26 @@ namespace dependent_static_var_template {
}

int cf() { return F<int>(); }

#ifdef CPP1Y
namespace GH55872 {
struct s {
template<typename T>
static CONST auto f = [] { return T::template g<s>; };
// expected-note@-1 {{in instantiation of static data member 'dependent_static_var_template::GH55872::t::g' requested here}}
// expected-note@-2 {{while substituting into a lambda expression here}}
};

struct t {
template<typename T>
static CONST auto g = [] { return T::template f<t>; };
// expected-error@-1 {{the type of variable template specialization 'f<dependent_static_var_template::GH55872::t>' declared with deduced type 'const auto' depends on itself}}
// expected-note@-2 {{while substituting into a lambda expression here}}
};

void test() { s::f<t>()(); } // expected-note {{in instantiation of static data member 'dependent_static_var_template::GH55872::s::f' requested here}}
}
#endif
}

#ifndef PRECXX11
Expand Down
17 changes: 17 additions & 0 deletions clang/test/SemaCXX/cxx1y-variable-templates_top_level.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -492,4 +492,21 @@ static_assert(C<int, 0,1,2,3,4>::VALUEARRAY[3] == 3, "");
static_assert(C<int, 0,1,2,3,4>::VALUEARRAY[0] == 0, "");

}

namespace appear_in_its_own_init {
template <class T>
auto GH51347 = GH51347<T>; // expected-error {{variable template 'GH51347' declared with deduced type 'auto' cannot appear in its own initializer}}

template <class T, class... Ts>
auto a = [] {
using U = T;
a<U, Ts...>; // expected-error {{variable template 'a' declared with deduced type 'auto' cannot appear in its own initializer}}
};

template <int...> int b;
template <int I>
auto b<I, I * 2, 5> = b<I, I * 2, 5l>; // expected-error {{variable template partial specialization 'b<I, I * 2, 5>' declared with deduced type 'auto' cannot appear in its own initializer}}
template <> auto b<0, 0, 0> = b<0, 0, 0>; // expected-error {{variable template explicit specialization 'b<0, 0, 0>' declared with deduced type 'auto' cannot appear in its own initializer}}
}

#endif
11 changes: 11 additions & 0 deletions clang/test/SemaTemplate/instantiate-var-template.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,14 @@ namespace InvalidInsertPos {
template<> int v<int, 0>;
int k = v<int, 500>;
}

namespace GH97881_comment {
template <bool B>
auto g = sizeof(g<!B>);
// expected-error@-1 {{the type of variable template specialization 'g<false>'}}
// expected-note@-2 {{in instantiation of variable template specialization 'GH97881_comment::g'}}

void test() {
(void)sizeof(g<false>); // expected-note {{in instantiation of variable template specialization 'GH97881_comment::g'}}
}
}