84 changes: 81 additions & 3 deletions clang/lib/AST/TemplateBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ static void printIntegral(const TemplateArgument &TemplArg,
// TemplateArgument Implementation
//===----------------------------------------------------------------------===//

TemplateArgument::TemplateArgument(ASTContext &Ctx, const llvm::APSInt &Value,
QualType Type) {
TemplateArgument::TemplateArgument(const ASTContext &Ctx,
const llvm::APSInt &Value, QualType Type) {
Integer.Kind = Integral;
// Copy the APSInt value into our decomposed form.
Integer.BitWidth = Value.getBitWidth();
Expand All @@ -103,6 +103,44 @@ TemplateArgument::TemplateArgument(ASTContext &Ctx, const llvm::APSInt &Value,
Integer.Type = Type.getAsOpaquePtr();
}

static const ValueDecl *getAsSimpleValueDeclRef(const ASTContext &Ctx,
QualType T, const APValue &V) {
// Pointers to members are relatively easy.
if (V.isMemberPointer() && V.getMemberPointerPath().empty())
return V.getMemberPointerDecl();

// We model class non-type template parameters as their template parameter
// object declaration.
if (V.isStruct() || V.isUnion())
return Ctx.getTemplateParamObjectDecl(T, V);

// Pointers and references with an empty path use the special 'Declaration'
// representation.
if (V.isLValue() && V.hasLValuePath() &&
V.getLValuePath().empty() && !V.isLValueOnePastTheEnd())
return V.getLValueBase().dyn_cast<const ValueDecl *>();

// Everything else uses the 'uncommon' representation.
return nullptr;
}

TemplateArgument::TemplateArgument(const ASTContext &Ctx, QualType Type,
const APValue &V) {
if (Type->isIntegralOrEnumerationType() && V.isInt())
*this = TemplateArgument(Ctx, V.getInt(), Type);
else if ((V.isLValue() && V.isNullPointer()) ||
(V.isMemberPointer() && !V.getMemberPointerDecl()))
*this = TemplateArgument(Type, /*isNullPtr=*/true);
else if (const ValueDecl *VD = getAsSimpleValueDeclRef(Ctx, Type, V))
// FIXME: The Declaration form should expose a const ValueDecl*.
*this = TemplateArgument(const_cast<ValueDecl*>(VD), Type);
else {
Value.Kind = UncommonValue;
Value.Value = new (Ctx) APValue(V);
Value.Type = Type.getAsOpaquePtr();
}
}

TemplateArgument
TemplateArgument::CreatePackCopy(ASTContext &Context,
ArrayRef<TemplateArgument> Args) {
Expand Down Expand Up @@ -134,6 +172,7 @@ TemplateArgumentDependence TemplateArgument::getDependence() const {
case NullPtr:
case Integral:
case Declaration:
case UncommonValue:
return TemplateArgumentDependence::None;

case Expression:
Expand Down Expand Up @@ -165,6 +204,7 @@ bool TemplateArgument::isPackExpansion() const {
case Null:
case Declaration:
case Integral:
case UncommonValue:
case Pack:
case Template:
case NullPtr:
Expand Down Expand Up @@ -215,6 +255,9 @@ QualType TemplateArgument::getNonTypeTemplateArgumentType() const {

case TemplateArgument::NullPtr:
return getNullPtrType();

case TemplateArgument::UncommonValue:
return getUncommonValueType();
}

llvm_unreachable("Invalid TemplateArgument Kind!");
Expand Down Expand Up @@ -259,8 +302,13 @@ void TemplateArgument::Profile(llvm::FoldingSetNodeID &ID,
}

case Integral:
getAsIntegral().Profile(ID);
getIntegralType().Profile(ID);
getAsIntegral().Profile(ID);
break;

case UncommonValue:
getUncommonValueType().Profile(ID);
getAsUncommonValue().Profile(ID);
break;

case Expression:
Expand Down Expand Up @@ -296,6 +344,16 @@ bool TemplateArgument::structurallyEquals(const TemplateArgument &Other) const {
return getIntegralType() == Other.getIntegralType() &&
getAsIntegral() == Other.getAsIntegral();

case UncommonValue: {
if (getUncommonValueType() != Other.getUncommonValueType())
return false;

llvm::FoldingSetNodeID A, B;
getAsUncommonValue().Profile(A);
Other.getAsUncommonValue().Profile(B);
return A == B;
}

case Pack:
if (Args.NumArgs != Other.Args.NumArgs) return false;
for (unsigned I = 0, E = Args.NumArgs; I != E; ++I)
Expand All @@ -322,6 +380,7 @@ TemplateArgument TemplateArgument::getPackExpansionPattern() const {

case Declaration:
case Integral:
case UncommonValue:
case Pack:
case Null:
case Template:
Expand Down Expand Up @@ -361,6 +420,10 @@ void TemplateArgument::print(const PrintingPolicy &Policy,
break;
}

case UncommonValue:
getAsUncommonValue().printPretty(Out, Policy, getUncommonValueType());
break;

case NullPtr:
Out << "nullptr";
break;
Expand Down Expand Up @@ -443,6 +506,9 @@ SourceRange TemplateArgumentLoc::getSourceRange() const {
case TemplateArgument::Integral:
return getSourceIntegralExpression()->getSourceRange();

case TemplateArgument::UncommonValue:
return getSourceUncommonValueExpression()->getSourceRange();

case TemplateArgument::Pack:
case TemplateArgument::Null:
return SourceRange();
Expand Down Expand Up @@ -471,6 +537,18 @@ static const T &DiagTemplateArg(const T &DB, const TemplateArgument &Arg) {
case TemplateArgument::Integral:
return DB << Arg.getAsIntegral().toString(10);

case TemplateArgument::UncommonValue: {
// FIXME: We're guessing at LangOptions!
SmallString<32> Str;
llvm::raw_svector_ostream OS(Str);
LangOptions LangOpts;
LangOpts.CPlusPlus = true;
PrintingPolicy Policy(LangOpts);
Arg.getAsUncommonValue().printPretty(OS, Policy,
Arg.getUncommonValueType());
return DB << OS.str();
}

case TemplateArgument::Template:
return DB << Arg.getAsTemplate();

Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/TypeLoc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,7 @@ void TemplateSpecializationTypeLoc::initializeArgLocs(ASTContext &Context,
case TemplateArgument::Integral:
case TemplateArgument::Declaration:
case TemplateArgument::NullPtr:
case TemplateArgument::UncommonValue:
ArgInfos[i] = TemplateArgumentLocInfo();
break;

Expand Down
8 changes: 8 additions & 0 deletions clang/lib/CodeGen/CGDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1953,6 +1953,14 @@ CGDebugInfo::CollectTemplateParams(const TemplateParameterList *TPList,
TemplateParams.push_back(DBuilder.createTemplateValueParameter(
TheCU, Name, TTy, defaultParameter, V));
} break;
case TemplateArgument::UncommonValue: {
QualType T = TA.getUncommonValueType();
llvm::DIType *TTy = getOrCreateType(T, Unit);
llvm::Constant *V = ConstantEmitter(CGM).emitAbstract(
SourceLocation(), TA.getAsUncommonValue(), T);
TemplateParams.push_back(DBuilder.createTemplateValueParameter(
TheCU, Name, TTy, defaultParameter, V));
} break;
case TemplateArgument::Template:
TemplateParams.push_back(DBuilder.createTemplateTemplateParameter(
TheCU, Name, nullptr,
Expand Down
10 changes: 5 additions & 5 deletions clang/lib/CodeGen/CGExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1366,11 +1366,11 @@ llvm::Constant *ConstantEmitter::tryEmitConstantExpr(const ConstantExpr *CE) {
if (!CE->hasAPValueResult())
return nullptr;
const Expr *Inner = CE->getSubExpr()->IgnoreImplicit();
QualType RetType;
if (auto *Call = dyn_cast<CallExpr>(Inner))
RetType = Call->getCallReturnType(CGF->getContext());
else if (auto *Ctor = dyn_cast<CXXConstructExpr>(Inner))
RetType = Ctor->getType();
QualType RetType = Inner->getType();
if (Inner->isLValue())
RetType = CGF->getContext().getLValueReferenceType(RetType);
else if (Inner->isXValue())
RetType = CGF->getContext().getRValueReferenceType(RetType);
llvm::Constant *Res =
emitAbstract(CE->getBeginLoc(), CE->getAPValueResult(), RetType);
return Res;
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Index/USRGeneration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -983,6 +983,10 @@ void USRGenerator::VisitTemplateArgument(const TemplateArgument &Arg) {
VisitType(Arg.getIntegralType());
Out << Arg.getAsIntegral();
break;

case TemplateArgument::UncommonValue:
// FIXME: Visit value.
break;
}
}

Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4135,7 +4135,11 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(Expr *E,

// The operand for sizeof and alignof is in an unevaluated expression context,
// so side effects could result in unintended consequences.
// Exclude instantiation-dependent expressions, because 'sizeof' is sometimes
// used to build SFINAE gadgets.
// FIXME: Should we consider instantiation-dependent operands to 'alignof'?
if (IsUnevaluatedOperand && !inTemplateInstantiation() &&
!E->isInstantiationDependent() &&
E->HasSideEffects(Context, false))
Diag(E->getExprLoc(), diag::warn_side_effects_unevaluated_context);

Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7691,7 +7691,8 @@ ExprResult Sema::BuildCXXNoexceptExpr(SourceLocation KeyLoc, Expr *Operand,

Operand = R.get();

if (!inTemplateInstantiation() && Operand->HasSideEffects(Context, false)) {
if (!inTemplateInstantiation() && !Operand->isInstantiationDependent() &&
Operand->HasSideEffects(Context, false)) {
// The expression operand for noexcept is in an unevaluated expression
// context, so side effects could result in unintended consequences.
Diag(Operand->getExprLoc(), diag::warn_side_effects_unevaluated_context);
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Sema/SemaLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2657,6 +2657,7 @@ addAssociatedClassesAndNamespaces(AssociatedLookup &Result,
case TemplateArgument::Integral:
case TemplateArgument::Expression:
case TemplateArgument::NullPtr:
case TemplateArgument::UncommonValue:
// [Note: non-type template arguments do not contribute to the set of
// associated namespaces. ]
break;
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/Sema/SemaOverload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5774,7 +5774,9 @@ static ExprResult CheckConvertedConstantExpression(Sema &S, Expr *From,

if (Notes.empty()) {
// It's a constant expression.
Expr *E = ConstantExpr::Create(S.Context, Result.get(), Value);
Expr *E = Result.get();
if (!isa<ConstantExpr>(E))
E = ConstantExpr::Create(S.Context, Result.get(), Value);
if (ReturnPreNarrowingValue)
Value = std::move(PreNarrowingValue);
return E;
Expand Down
200 changes: 127 additions & 73 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4086,6 +4086,7 @@ static bool isTemplateArgumentTemplateParameter(
case TemplateArgument::NullPtr:
case TemplateArgument::Integral:
case TemplateArgument::Declaration:
case TemplateArgument::UncommonValue:
case TemplateArgument::Pack:
case TemplateArgument::TemplateExpansion:
return false;
Expand Down Expand Up @@ -5419,6 +5420,7 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param,

case TemplateArgument::Declaration:
case TemplateArgument::Integral:
case TemplateArgument::UncommonValue:
case TemplateArgument::NullPtr:
// We've already checked this template argument, so just copy
// it to the list of converted arguments.
Expand Down Expand Up @@ -5565,11 +5567,10 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param,
return true;

case TemplateArgument::Declaration:
llvm_unreachable("Declaration argument with template template parameter");
case TemplateArgument::Integral:
llvm_unreachable("Integral argument with template template parameter");
case TemplateArgument::UncommonValue:
case TemplateArgument::NullPtr:
llvm_unreachable("Null pointer argument with template template parameter");
llvm_unreachable("non-type argument with template template parameter");

case TemplateArgument::Pack:
llvm_unreachable("Caller must expand template argument packs");
Expand Down Expand Up @@ -6936,37 +6937,9 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
return ArgResult;
}

// Convert the APValue to a TemplateArgument.
switch (Value.getKind()) {
case APValue::None:
assert(ParamType->isNullPtrType());
Converted = TemplateArgument(CanonParamType, /*isNullPtr*/true);
break;
case APValue::Indeterminate:
llvm_unreachable("result of constant evaluation should be initialized");
break;
case APValue::Int:
assert(ParamType->isIntegralOrEnumerationType());
Converted = TemplateArgument(Context, Value.getInt(), CanonParamType);
break;
case APValue::MemberPointer: {
assert(ParamType->isMemberPointerType());

// FIXME: We need TemplateArgument representation and mangling for these.
if (!Value.getMemberPointerPath().empty()) {
Diag(Arg->getBeginLoc(),
diag::err_template_arg_member_ptr_base_derived_not_supported)
<< Value.getMemberPointerDecl() << ParamType
<< Arg->getSourceRange();
return ExprError();
}

auto *VD = const_cast<ValueDecl*>(Value.getMemberPointerDecl());
Converted = VD ? TemplateArgument(VD, CanonParamType)
: TemplateArgument(CanonParamType, /*isNullPtr*/true);
break;
}
case APValue::LValue: {
// Prior to C++20, enforce restrictions on possible template argument
// values.
if (!getLangOpts().CPlusPlus20 && Value.isLValue()) {
// For a non-type template-parameter of pointer or reference type,
// the value of the constant expression shall not refer to
assert(ParamType->isPointerType() || ParamType->isReferenceType() ||
Expand All @@ -6982,8 +6955,7 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
<< Arg->getSourceRange();
return ExprError();
}
// -- a subobject
// FIXME: Until C++20
// -- a subobject [until C++20]
if (Value.hasLValuePath() && Value.getLValuePath().size() == 1 &&
VD && VD->getType()->isArrayType() &&
Value.getLValuePath()[0].getAsArrayIndex() == 0 &&
Expand All @@ -7001,29 +6973,12 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
"null reference should not be a constant expression");
assert((!VD || !ParamType->isNullPtrType()) &&
"non-null value of type nullptr_t?");
Converted = VD ? TemplateArgument(VD, CanonParamType)
: TemplateArgument(CanonParamType, /*isNullPtr*/true);
break;
}
case APValue::Struct:
case APValue::Union:
// Get or create the corresponding template parameter object.
Converted = TemplateArgument(
Context.getTemplateParamObjectDecl(CanonParamType, Value),
CanonParamType);
break;
case APValue::AddrLabelDiff:

if (Value.isAddrLabelDiff())
return Diag(StartLoc, diag::err_non_type_template_arg_addr_label_diff);
case APValue::FixedPoint:
case APValue::Float:
case APValue::ComplexInt:
case APValue::ComplexFloat:
case APValue::Vector:
case APValue::Array:
return Diag(StartLoc, diag::err_non_type_template_arg_unsupported)
<< ParamType;
}

Converted = TemplateArgument(Context, CanonParamType, Value);
return ArgResult.get();
}

Expand Down Expand Up @@ -7561,12 +7516,9 @@ Sema::BuildExpressionFromDeclTemplateArgument(const TemplateArgument &Arg,
/// This routine takes care of the mapping from an integral template
/// argument (which may have any integral type) to the appropriate
/// literal value.
ExprResult
Sema::BuildExpressionFromIntegralTemplateArgument(const TemplateArgument &Arg,
SourceLocation Loc) {
assert(Arg.getKind() == TemplateArgument::Integral &&
"Operation is only valid for integral template arguments");
QualType OrigT = Arg.getIntegralType();
static Expr *BuildExpressionFromIntegralTemplateArgumentValue(
Sema &S, QualType OrigT, const llvm::APSInt &Int, SourceLocation Loc) {
assert(OrigT->isIntegralOrEnumerationType());

// If this is an enum type that we're instantiating, we need to use an integer
// type the same size as the enumerator. We don't want to build an
Expand All @@ -7582,7 +7534,7 @@ Sema::BuildExpressionFromIntegralTemplateArgument(const TemplateArgument &Arg,
CharacterLiteral::CharacterKind Kind;
if (T->isWideCharType())
Kind = CharacterLiteral::Wide;
else if (T->isChar8Type() && getLangOpts().Char8)
else if (T->isChar8Type() && S.getLangOpts().Char8)
Kind = CharacterLiteral::UTF8;
else if (T->isChar16Type())
Kind = CharacterLiteral::UTF16;
Expand All @@ -7591,29 +7543,131 @@ Sema::BuildExpressionFromIntegralTemplateArgument(const TemplateArgument &Arg,
else
Kind = CharacterLiteral::Ascii;

E = new (Context) CharacterLiteral(Arg.getAsIntegral().getZExtValue(),
Kind, T, Loc);
E = new (S.Context) CharacterLiteral(Int.getZExtValue(), Kind, T, Loc);
} else if (T->isBooleanType()) {
E = new (Context) CXXBoolLiteralExpr(Arg.getAsIntegral().getBoolValue(),
T, Loc);
} else if (T->isNullPtrType()) {
E = new (Context) CXXNullPtrLiteralExpr(Context.NullPtrTy, Loc);
E = new (S.Context) CXXBoolLiteralExpr(Int.getBoolValue(), T, Loc);
} else {
E = IntegerLiteral::Create(Context, Arg.getAsIntegral(), T, Loc);
E = IntegerLiteral::Create(S.Context, Int, T, Loc);
}

if (OrigT->isEnumeralType()) {
// FIXME: This is a hack. We need a better way to handle substituted
// non-type template parameters.
E = CStyleCastExpr::Create(Context, OrigT, VK_RValue, CK_IntegralCast, E,
nullptr, CurFPFeatureOverrides(),
Context.getTrivialTypeSourceInfo(OrigT, Loc),
E = CStyleCastExpr::Create(S.Context, OrigT, VK_RValue, CK_IntegralCast, E,
nullptr, S.CurFPFeatureOverrides(),
S.Context.getTrivialTypeSourceInfo(OrigT, Loc),
Loc, Loc);
}

return E;
}

static Expr *BuildExpressionFromNonTypeTemplateArgumentValue(
Sema &S, QualType T, const APValue &Val, SourceLocation Loc) {
auto MakeInitList = [&] (ArrayRef<Expr*> Elts) -> Expr* {
auto *ILE = new (S.Context) InitListExpr(S.Context, Loc, Elts, Loc);
ILE->setType(T);
return ILE;
};

switch (Val.getKind()) {
case APValue::AddrLabelDiff:
// This cannot occur in a template argument at all.
case APValue::Array:
case APValue::Struct:
case APValue::Union:
// These can only occur within a template parameter object, which is
// represented as a TemplateArgument::Declaration.
llvm_unreachable("unexpected template argument value");

case APValue::Int:
return BuildExpressionFromIntegralTemplateArgumentValue(S, T, Val.getInt(),
Loc);

case APValue::Float:
return FloatingLiteral::Create(S.Context, Val.getFloat(), /*IsExact=*/true,
T, Loc);

case APValue::FixedPoint:
return FixedPointLiteral::CreateFromRawInt(
S.Context, Val.getFixedPoint().getValue(), T, Loc,
Val.getFixedPoint().getScale());

case APValue::ComplexInt: {
QualType ElemT = T->castAs<ComplexType>()->getElementType();
return MakeInitList({BuildExpressionFromIntegralTemplateArgumentValue(
S, ElemT, Val.getComplexIntReal(), Loc),
BuildExpressionFromIntegralTemplateArgumentValue(
S, ElemT, Val.getComplexIntImag(), Loc)});
}

case APValue::ComplexFloat: {
QualType ElemT = T->castAs<ComplexType>()->getElementType();
return MakeInitList(
{FloatingLiteral::Create(S.Context, Val.getComplexFloatReal(), true,
ElemT, Loc),
FloatingLiteral::Create(S.Context, Val.getComplexFloatImag(), true,
ElemT, Loc)});
}

case APValue::Vector: {
QualType ElemT = T->castAs<VectorType>()->getElementType();
llvm::SmallVector<Expr *, 8> Elts;
for (unsigned I = 0, N = Val.getVectorLength(); I != N; ++I)
Elts.push_back(BuildExpressionFromNonTypeTemplateArgumentValue(
S, ElemT, Val.getVectorElt(I), Loc));
return MakeInitList(Elts);
}

case APValue::None:
case APValue::Indeterminate:
// FIXME: Are these values possible?
case APValue::LValue:
case APValue::MemberPointer:
// There isn't necessarily a valid equivalent source-level syntax for
// these; in particular, a naive lowering might violate access control.
// So for now we lower to a ConstantExpr holding the value, wrapped around
// an OpaqueValueExpr.
// FIXME: We should have a better representation for this.
ExprValueKind VK = VK_RValue;
if (T->isReferenceType()) {
T = T->getPointeeType();
VK = VK_LValue;
}
auto *OVE = new (S.Context) OpaqueValueExpr(Loc, T, VK);
return ConstantExpr::Create(S.Context, OVE, Val);
}
}

ExprResult
Sema::BuildExpressionFromNonTypeTemplateArgument(const TemplateArgument &Arg,
SourceLocation Loc) {
switch (Arg.getKind()) {
case TemplateArgument::Null:
case TemplateArgument::Type:
case TemplateArgument::Template:
case TemplateArgument::TemplateExpansion:
case TemplateArgument::Pack:
llvm_unreachable("not a non-type template argument");

case TemplateArgument::Expression:
return Arg.getAsExpr();

case TemplateArgument::NullPtr:
case TemplateArgument::Declaration:
return BuildExpressionFromDeclTemplateArgument(
Arg, Arg.getNonTypeTemplateArgumentType(), Loc);

case TemplateArgument::Integral:
return BuildExpressionFromIntegralTemplateArgumentValue(
*this, Arg.getIntegralType(), Arg.getAsIntegral(), Loc);

case TemplateArgument::UncommonValue:
return BuildExpressionFromNonTypeTemplateArgumentValue(
*this, Arg.getUncommonValueType(), Arg.getAsUncommonValue(), Loc);
}
}

/// Match two template parameters within template parameter lists.
static bool MatchTemplateParameterKind(Sema &S, NamedDecl *New, NamedDecl *Old,
bool Complain,
Expand Down
94 changes: 54 additions & 40 deletions clang/lib/Sema/SemaTemplateDeduction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,16 @@ checkDeducedTemplateArguments(ASTContext &Context,
// All other combinations are incompatible.
return DeducedTemplateArgument();

case TemplateArgument::UncommonValue:
// If we deduced a value and a dependent expression, keep the value.
if (Y.getKind() == TemplateArgument::Expression ||
(Y.getKind() == TemplateArgument::UncommonValue &&
X.structurallyEquals(Y)))
return X;

// All other combinations are incompatible.
return DeducedTemplateArgument();

case TemplateArgument::Template:
if (Y.getKind() == TemplateArgument::Template &&
Context.hasSameTemplateName(X.getAsTemplate(), Y.getAsTemplate()))
Expand Down Expand Up @@ -2361,20 +2371,18 @@ DeduceTemplateArguments(Sema &S,
return Sema::TDK_NonDeducedMismatch;

case TemplateArgument::Integral:
if (Arg.getKind() == TemplateArgument::Integral) {
if (hasSameExtendedValue(Param.getAsIntegral(), Arg.getAsIntegral()))
return Sema::TDK_Success;
if (Arg.getKind() == TemplateArgument::Integral &&
hasSameExtendedValue(Param.getAsIntegral(), Arg.getAsIntegral()))
return Sema::TDK_Success;

Info.FirstArg = Param;
Info.SecondArg = Arg;
return Sema::TDK_NonDeducedMismatch;
}
Info.FirstArg = Param;
Info.SecondArg = Arg;
return Sema::TDK_NonDeducedMismatch;

if (Arg.getKind() == TemplateArgument::Expression) {
Info.FirstArg = Param;
Info.SecondArg = Arg;
return Sema::TDK_NonDeducedMismatch;
}
case TemplateArgument::UncommonValue:
if (Arg.getKind() == TemplateArgument::UncommonValue &&
Arg.structurallyEquals(Param))
return Sema::TDK_Success;

Info.FirstArg = Param;
Info.SecondArg = Arg;
Expand All @@ -2383,28 +2391,34 @@ DeduceTemplateArguments(Sema &S,
case TemplateArgument::Expression:
if (const NonTypeTemplateParmDecl *NTTP =
getDeducedParameterFromExpr(Info, Param.getAsExpr())) {
if (Arg.getKind() == TemplateArgument::Integral)
return DeduceNonTypeTemplateArgument(S, TemplateParams, NTTP,
Arg.getAsIntegral(),
Arg.getIntegralType(),
/*ArrayBound=*/false,
Info, Deduced);
if (Arg.getKind() == TemplateArgument::NullPtr)
return DeduceNullPtrTemplateArgument(S, TemplateParams, NTTP,
Arg.getNullPtrType(),
Info, Deduced);
if (Arg.getKind() == TemplateArgument::Expression)
return DeduceNonTypeTemplateArgument(S, TemplateParams, NTTP,
Arg.getAsExpr(), Info, Deduced);
if (Arg.getKind() == TemplateArgument::Declaration)
return DeduceNonTypeTemplateArgument(S, TemplateParams, NTTP,
Arg.getAsDecl(),
Arg.getParamTypeForDecl(),
Info, Deduced);
switch (Arg.getKind()) {
case TemplateArgument::Integral:
case TemplateArgument::Expression:
case TemplateArgument::UncommonValue:
return DeduceNonTypeTemplateArgument(
S, TemplateParams, NTTP, DeducedTemplateArgument(Arg),
Arg.getNonTypeTemplateArgumentType(), Info, Deduced);

Info.FirstArg = Param;
Info.SecondArg = Arg;
return Sema::TDK_NonDeducedMismatch;
case TemplateArgument::NullPtr:
return DeduceNullPtrTemplateArgument(
S, TemplateParams, NTTP, Arg.getNullPtrType(), Info, Deduced);


case TemplateArgument::Declaration:
return DeduceNonTypeTemplateArgument(
S, TemplateParams, NTTP, Arg.getAsDecl(), Arg.getParamTypeForDecl(),
Info, Deduced);

case TemplateArgument::Null:
case TemplateArgument::Type:
case TemplateArgument::Template:
case TemplateArgument::TemplateExpansion:
case TemplateArgument::Pack:
Info.FirstArg = Param;
Info.SecondArg = Arg;
return Sema::TDK_NonDeducedMismatch;
}
llvm_unreachable("Unknown template argument kind");
}

// Can't deduce anything, but that's okay.
Expand Down Expand Up @@ -2592,6 +2606,9 @@ static bool isSameTemplateArg(ASTContext &Context,
case TemplateArgument::Integral:
return hasSameExtendedValue(X.getAsIntegral(), Y.getAsIntegral());

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

case TemplateArgument::Expression: {
llvm::FoldingSetNodeID XID, YID;
X.getAsExpr()->Profile(XID, Context, true);
Expand Down Expand Up @@ -2657,9 +2674,9 @@ Sema::getTrivialTemplateArgumentLoc(const TemplateArgument &Arg,
E);
}

case TemplateArgument::Integral: {
Expr *E =
BuildExpressionFromIntegralTemplateArgument(Arg, Loc).getAs<Expr>();
case TemplateArgument::Integral:
case TemplateArgument::UncommonValue: {
Expr *E = BuildExpressionFromNonTypeTemplateArgument(Arg, Loc).get();
return TemplateArgumentLoc(TemplateArgument(E), E);
}

Expand Down Expand Up @@ -6103,11 +6120,8 @@ MarkUsedTemplateParameters(ASTContext &Ctx,
case TemplateArgument::Null:
case TemplateArgument::Integral:
case TemplateArgument::Declaration:
break;

case TemplateArgument::NullPtr:
MarkUsedTemplateParameters(Ctx, TemplateArg.getNullPtrType(), OnlyDeduced,
Depth, Used);
case TemplateArgument::UncommonValue:
break;

case TemplateArgument::Type:
Expand Down
8 changes: 5 additions & 3 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1556,16 +1556,18 @@ ExprResult TemplateInstantiator::transformNonTypeTemplateParmRef(
VD = nullptr;
}

QualType paramType = VD ? arg.getParamTypeForDecl() : arg.getNullPtrType();
QualType paramType = arg.getNonTypeTemplateArgumentType();
assert(!paramType.isNull() && "type substitution failed for param type");
assert(!paramType->isDependentType() && "param type still dependent");
result = SemaRef.BuildExpressionFromDeclTemplateArgument(arg, paramType, loc);
refParam = paramType->isReferenceType();
} else {
result = SemaRef.BuildExpressionFromIntegralTemplateArgument(arg, loc);
QualType paramType = arg.getNonTypeTemplateArgumentType();
result = SemaRef.BuildExpressionFromNonTypeTemplateArgument(arg, loc);
refParam = paramType->isReferenceType();
assert(result.isInvalid() ||
SemaRef.Context.hasSameType(result.get()->getType(),
arg.getIntegralType()));
paramType.getNonReferenceType()));
}

if (result.isInvalid())
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Sema/SemaTemplateVariadic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1103,6 +1103,7 @@ Sema::getTemplateArgumentPackExpansionPattern(
case TemplateArgument::NullPtr:
case TemplateArgument::Template:
case TemplateArgument::Integral:
case TemplateArgument::UncommonValue:
case TemplateArgument::Pack:
case TemplateArgument::Null:
return TemplateArgumentLoc();
Expand Down Expand Up @@ -1153,6 +1154,7 @@ Optional<unsigned> Sema::getFullyPackExpandedSize(TemplateArgument Arg) {
case TemplateArgument::NullPtr:
case TemplateArgument::TemplateExpansion:
case TemplateArgument::Integral:
case TemplateArgument::UncommonValue:
case TemplateArgument::Pack:
case TemplateArgument::Null:
return None;
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/Sema/SemaType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8993,9 +8993,11 @@ QualType Sema::BuildDecltypeType(Expr *E, SourceLocation Loc,
assert(!E->hasPlaceholderType() && "unexpected placeholder");

if (AsUnevaluated && CodeSynthesisContexts.empty() &&
E->HasSideEffects(Context, false)) {
!E->isInstantiationDependent() && E->HasSideEffects(Context, false)) {
// The expression operand for decltype is in an unevaluated expression
// context, so side effects could result in unintended consequences.
// Exclude instantiation-dependent expressions, because 'decltype' is often
// used to build SFINAE gadgets.
Diag(E->getExprLoc(), diag::warn_side_effects_unevaluated_context);
}

Expand Down
12 changes: 10 additions & 2 deletions clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -3557,6 +3557,7 @@ class TreeTransform {
case TemplateArgument::Null:
case TemplateArgument::Integral:
case TemplateArgument::Declaration:
case TemplateArgument::UncommonValue:
case TemplateArgument::Pack:
case TemplateArgument::TemplateExpansion:
case TemplateArgument::NullPtr:
Expand Down Expand Up @@ -4229,7 +4230,8 @@ bool TreeTransform<Derived>::TransformTemplateArgument(

case TemplateArgument::Integral:
case TemplateArgument::NullPtr:
case TemplateArgument::Declaration: {
case TemplateArgument::Declaration:
case TemplateArgument::UncommonValue: {
// Transform a resolved template argument straight to a resolved template
// argument. We get here when substituting into an already-substituted
// template type argument during concept satisfaction checking.
Expand All @@ -4256,9 +4258,15 @@ bool TreeTransform<Derived>::TransformTemplateArgument(
else if (Arg.getKind() == TemplateArgument::NullPtr)
Output = TemplateArgumentLoc(TemplateArgument(NewT, /*IsNullPtr=*/true),
TemplateArgumentLocInfo());
else
else if (Arg.getKind() == TemplateArgument::Declaration)
Output = TemplateArgumentLoc(TemplateArgument(NewD, NewT),
TemplateArgumentLocInfo());
else if (Arg.getKind() == TemplateArgument::UncommonValue)
Output = TemplateArgumentLoc(
TemplateArgument(getSema().Context, NewT, Arg.getAsUncommonValue()),
TemplateArgumentLocInfo());
else
llvm_unreachable("unexpected template argument kind");

return false;
}
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Serialization/ASTReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7100,6 +7100,7 @@ ASTRecordReader::readTemplateArgumentLocInfo(TemplateArgument::ArgKind Kind) {
case TemplateArgument::Integral:
case TemplateArgument::Declaration:
case TemplateArgument::NullPtr:
case TemplateArgument::UncommonValue:
case TemplateArgument::Pack:
// FIXME: Is this right?
return TemplateArgumentLocInfo();
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Serialization/ASTWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5350,6 +5350,7 @@ void ASTRecordWriter::AddTemplateArgumentLocInfo(
case TemplateArgument::Integral:
case TemplateArgument::Declaration:
case TemplateArgument::NullPtr:
case TemplateArgument::UncommonValue:
case TemplateArgument::Pack:
// FIXME: Is this right?
break;
Expand Down
10 changes: 10 additions & 0 deletions clang/test/CodeGenCXX/mangle-ms-templates.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// RUN: %clang_cc1 -std=c++11 -fms-compatibility-version=19 -emit-llvm %s -o - -fms-extensions -fdelayed-template-parsing -triple=x86_64-pc-win32 | FileCheck -check-prefix X64 %s
// RUN: %clang_cc1 -std=c++17 -fms-compatibility-version=19 -emit-llvm %s -o - -fms-extensions -fdelayed-template-parsing -triple=i386-pc-win32 | FileCheck %s
// RUN: %clang_cc1 -std=c++17 -fms-compatibility-version=19 -emit-llvm %s -o - -fms-extensions -fdelayed-template-parsing -triple=x86_64-pc-win32 | FileCheck -check-prefix X64 %s
// RUN: %clang_cc1 -std=c++20 -fms-compatibility-version=19 -emit-llvm %s -o - -fms-extensions -fdelayed-template-parsing -triple=x86_64-pc-win32 | FileCheck -check-prefix CXX20-X64 %s

template<typename T>
class Class {
Expand Down Expand Up @@ -327,3 +328,12 @@ void fun_uint128(UInt128<(unsigned __int128)-1>) {}
// X64: define {{.*}} @"?fun_uint128@@YAXU?$UInt128@$0DPPPPPPPPPPPPPPPAAAAAAAAAAAAAAAB@@@@Z"(
void fun_uint128(UInt128<(unsigned __int128)9223372036854775807 * (unsigned __int128)9223372036854775807>) {}
#endif

#if __cplusplus >= 202002L
template<float> struct Float {};
// CXX20-X64: define {{.*}} @"?f@@YAXU?$Float@$ADPIAAAAA@@@@Z"(
void f(Float<1.0f>) {}
template<auto> struct Auto {};
// CXX20-X64: define {{.*}} @"?f@@YAXU?$Auto@$MMADPIAAAAA@@@@Z"(
void f(Auto<1.0f>) {}
#endif
40 changes: 39 additions & 1 deletion clang/test/CodeGenCXX/mangle-template.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,16 @@ namespace test16 {
namespace cxx20 {
template<auto> struct A {};
template<typename T, T V> struct B {};
// CXX20: define {{.*}} @_ZN5cxx201fENS_1AILf3f800000EEE(
void f(A<1.0f>) {}
// CXX20: define {{.*}} @_ZN5cxx201fENS_1AILd3ff0000000000000EEE(
void f(A<1.0>) {}
// CXX20: define {{.*}} @_ZN5cxx201fENS_1AILe3fff8000000000000000EEE(
void f(A<1.0l>) {}
// CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXtlCiLi0ELi1EEEEE(
void f(A<1i>) {}
// CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXtlCdLd0000000000000000ELd3ff0000000000000EEEEE(
void f(A<1.0i>) {}

int x;
// CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXadL_ZNS_1xEEEEE(
Expand All @@ -245,7 +255,24 @@ namespace cxx20 {
// CXX20: define {{.*}} @_ZN5cxx201fENS_1BIPKvXadL_ZNS_1xEEEEE(
void f(B<const void*, (const void*)&x>) {}

struct Q { int x; };
struct Q { int x; } q;

// CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXadsoiL_ZNS_1qEEEEEE(
void f(A<&q.x>) {}
// CXX20: define {{.*}} @_ZN5cxx201fENS_1BIPiXadsoiL_ZNS_1qEEEEEE(
void f(B<int*, &q.x>) {}
// CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXadsoKiL_ZNS_1qEEEEEE(
void f(A<(const int*)&q.x>) {}
// CXX20: define {{.*}} @_ZN5cxx201fENS_1BIPKiXadsoS1_L_ZNS_1qEEEEEE
void f(B<const int*, (const int*)&q.x>) {}
// CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXcvPvadsoiL_ZNS_1qEEEEEE(
void f(A<(void*)&q.x>) {}
// CXX20: define {{.*}} @_ZN5cxx201fENS_1BIPvXadsoiL_ZNS_1qEEEEEE(
void f(B<void*, (void*)&q.x>) {}
// CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXcvPKvadsoiL_ZNS_1qEEEEEE(
void f(A<(const void*)&q.x>) {}
// CXX20: define {{.*}} @_ZN5cxx201fENS_1BIPKvXadsoiL_ZNS_1qEEEEEE(
void f(B<const void*, (const void*)&q.x>) {}

// CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXadL_ZNS_1Q1xEEEEE(
void f(A<&Q::x>) {}
Expand All @@ -255,6 +282,17 @@ namespace cxx20 {
void f(A<(const int Q::*)&Q::x>) {}
// CXX20: define {{.*}} @_ZN5cxx201fENS_1BIMNS_1QEKiXadL_ZNS1_1xEEEEE(
void f(B<const int Q::*, (const int Q::*)&Q::x>) {}

struct R : Q {};

// CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXmcMNS_1REiadL_ZNS_1Q1xEEEEEE(
void f(A<(int R::*)&Q::x>) {}
// CXX20: define {{.*}} @_ZN5cxx201fENS_1BIMNS_1REiXmcS2_adL_ZNS_1Q1xEEEEEE(
void f(B<int R::*, (int R::*)&Q::x>) {}
// CXX20: define {{.*}} @_ZN5cxx201fENS_1AIXmcMNS_1REKiadL_ZNS_1Q1xEEEEEE(
void f(A<(const int R::*)&Q::x>) {}
// CXX20: define {{.*}} @_ZN5cxx201fENS_1BIMNS_1REKiXmcS3_adL_ZNS_1Q1xEEEEEE(
void f(B<const int R::*, (const int R::*)&Q::x>) {}
}
#endif

Expand Down
81 changes: 81 additions & 0 deletions clang/test/CodeGenCXX/template-arguments.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// RUN: %clang_cc1 -std=c++20 %s -emit-llvm -o - -triple x86_64-linux -DCONSTEXPR= | FileCheck %s
// RUN: %clang_cc1 -std=c++20 %s -emit-llvm -o - -triple x86_64-linux -DCONSTEXPR=constexpr | FileCheck %s --check-prefix=CONST

template<typename T> CONSTEXPR T id(T v) { return v; }
template<auto V> auto value = id(V);

// CHECK: call {{.*}} @_Z2idIiET_S0_(i32 1)
// CONST: @_Z5valueILi1EE = weak_odr {{.*}} i32 1,
template int value<1>;

// CHECK: call {{.*}} @_Z2idIyET_S0_(i64 -1)
// CONST: @_Z5valueILy18446744073709551615EE = weak_odr {{.*}} i64 -1,
template unsigned long long value<-1ULL>;

// CHECK: call {{.*}} @_Z2idIfET_S0_(float 1.000000e+00)
// CONST: @_Z5valueILf3f800000EE = weak_odr {{.*}} float 1.000000e+00,
template float value<1.0f>;
// CHECK: call {{.*}} @_Z2idIdET_S0_(double 1.000000e+00)
// CONST: @_Z5valueILd3ff0000000000000EE = weak_odr {{.*}} double 1.000000e+00,
template double value<1.0>;

int n;
// CHECK: call {{.*}} @_Z2idIPiET_S1_(i32* @n)
// CONST: @_Z5valueIXadL_Z1nEEE = weak_odr {{.*}} i32* @n,
template int *value<&n>;

struct A { int a[3]; } a;
// CHECK: call {{.*}} @_Z2idIPiET_S1_(i32* getelementptr inbounds (%struct.A, %struct.A* @a, i32 0, i32 0, i32 0))
// CONST: @_Z5valueIXadsoiL_Z1aEEEE = weak_odr {{.*}} i32* getelementptr inbounds (%struct.A, %struct.A* @a, i32 0, i32 0, i32 0),
template int *value<&a.a[0]>;
// CHECK: call {{.*}} @_Z2idIPiET_S1_(i32* bitcast (i8* getelementptr (i8, i8* bitcast (%struct.A* @a to i8*), i64 4) to i32*))
// CONST: @_Z5valueIXadsoiL_Z1aE4EEE = weak_odr {{.*}} i32* bitcast (i8* getelementptr (i8, i8* bitcast (%struct.A* @a to i8*), i64 4) to i32*),
template int *value<&a.a[1]>;
// CHECK: call {{.*}} @_Z2idIPiET_S1_(i32* bitcast (i8* getelementptr (i8, i8* bitcast (%struct.A* @a to i8*), i64 8) to i32*))
// CONST: @_Z5valueIXadsoiL_Z1aE8EEE = weak_odr {{.*}} i32* bitcast (i8* getelementptr (i8, i8* bitcast (%struct.A* @a to i8*), i64 8) to i32*),
template int *value<&a.a[2]>;
// CHECK: call {{.*}} @_Z2idIPiET_S1_(i32* bitcast (i8* getelementptr (i8, i8* bitcast (%struct.A* @a to i8*), i64 12) to i32*))
// CONST: @_Z5valueIXadsoiL_Z1aE12pEEE = weak_odr {{.*}} i32* bitcast (i8* getelementptr (i8, i8* bitcast (%struct.A* @a to i8*), i64 12) to i32*),
template int *value<&a.a[3]>;

struct B { int x, y; };
// CHECK: call {{.*}} @_Z2idIM1BiET_S2_(i64 0)
// CONST: @_Z5valueIXadL_ZN1B1xEEEE = weak_odr {{.*}} i64 0,
template int B::*value<&B::x>;
// CHECK: call {{.*}} @_Z2idIM1BiET_S2_(i64 4)
// CONST: @_Z5valueIXadL_ZN1B1yEEEE = weak_odr {{.*}} i64 4,
template int B::*value<&B::y>;

struct C : A, B { int z; };
// CHECK: call {{.*}} @_Z2idIM1CiET_S2_(i64 12)
// CONST: @_Z5valueIXmcM1CiadL_ZN1B1xEE12EEE = weak_odr {{.*}} i64 12,
template int C::*value<(int C::*)&B::x>;
// CHECK: call {{.*}} @_Z2idIM1BiET_S2_(i64 8)
// CONST: @_Z5valueIXmcM1BiadL_ZN1C1zEEn12EEE = weak_odr {{.*}} i64 8,
template int B::*value<(int B::*)&C::z>;

// CHECK: store i32 1, i32*
// CHECK: store i32 2, i32*
// CHECK: bitcast { i32, i32 }* %{{.*}} to i64*
// CHECK: load i64,
// CHECK: call {{.*}} @_Z2idICiET_S1_(i64 %
// CONST: @_Z5valueIXtlCiLi1ELi2EEEE = weak_odr {{.*}} { i32, i32 } { i32 1, i32 2 },
template _Complex int value<1 + 2j>;

// CHECK: store float 1.000000e+00, float*
// CHECK: store float 2.000000e+00, float*
// CHECK: bitcast { float, float }* %{{.*}} to <2 x float>*
// CHECK: load <2 x float>,
// CHECK: call {{.*}} @_Z2idICfET_S1_(<2 x float> %
// CONST: @_Z5valueIXtlCfLf3f800000ELf40000000EEEE = weak_odr {{.*}} { float, float } { float 1.000000e+00, float 2.000000e+00 },
template _Complex float value<1.0f + 2.0fj>;

using V3i __attribute__((ext_vector_type(3))) = int;
// CHECK: call {{.*}} @_Z2idIDv3_iET_S1_(<3 x i32> <i32 1, i32 2, i32 3>)
// CONST: @_Z5valueIXtlDv3_iLi1ELi2ELi3EEEE = weak_odr {{.*}} <3 x i32> <i32 1, i32 2, i32 3>
template V3i value<V3i{1, 2, 3}>;

using V3f [[gnu::vector_size(12)]] = float;
// CHECK: call {{.*}} @_Z2idIDv3_fET_S1_(<3 x float> <float 1.000000e+00, float 2.000000e+00, float 3.000000e+00>)
// CONST: @_Z5valueIXtlDv3_fLf3f800000ELf40000000ELf40400000EEEE = weak_odr {{.*}} <3 x float> <float 1.000000e+00, float 2.000000e+00, float 3.000000e+00>
template V3f value<V3f{1, 2, 3}>;
40 changes: 19 additions & 21 deletions clang/test/SemaTemplate/temp_arg_nontype_cxx17.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

template<typename T, T val> struct A {};

template<typename T, typename U> constexpr bool is_same = false; // expected-note +{{here}}
template<typename T, typename U> constexpr bool is_same = false;
template<typename T> constexpr bool is_same<T, T> = true;

namespace String {
Expand Down Expand Up @@ -84,34 +84,32 @@ namespace PtrMem {
constexpr int B::*b = &B::b;
constexpr int C::*cb = b;
constexpr int D::*db = b;
constexpr int E::*ecb = cb; // expected-note +{{here}}
constexpr int E::*edb = db; // expected-note +{{here}}
constexpr int E::*ecb = cb;
constexpr int E::*edb = db;

constexpr int E::*e = &E::e;
constexpr int D::*de = (int D::*)e;
constexpr int C::*ce = (int C::*)e;
constexpr int B::*bde = (int B::*)de; // expected-note +{{here}}
constexpr int B::*bce = (int B::*)ce; // expected-note +{{here}}
constexpr int B::*bde = (int B::*)de;
constexpr int B::*bce = (int B::*)ce;

// FIXME: This should all be accepted, but we don't yet have a representation
// nor mangling for this form of template argument.
using Ab = A<int B::*, b>;
using Ab = A<int B::*, &B::b>;
using Abce = A<int B::*, bce>; // expected-error {{not supported}}
using Abde = A<int B::*, bde>; // expected-error {{not supported}}
static_assert(!is_same<Ab, Abce>, ""); // expected-error {{undeclared}} expected-error {{must be a type}}
static_assert(!is_same<Ab, Abde>, ""); // expected-error {{undeclared}} expected-error {{must be a type}}
static_assert(!is_same<Abce, Abde>, ""); // expected-error 2{{undeclared}} expected-error {{must be a type}}
static_assert(is_same<Abce, A<int B::*, (int B::*)(int C::*)&E::e>>, ""); // expected-error {{undeclared}} expected-error {{not supported}}
using Abce = A<int B::*, bce>;
using Abde = A<int B::*, bde>;
static_assert(!is_same<Ab, Abce>, "");
static_assert(!is_same<Ab, Abde>, "");
static_assert(!is_same<Abce, Abde>, "");
static_assert(is_same<Abce, A<int B::*, (int B::*)(int C::*)&E::e>>, "");

using Ae = A<int E::*, e>;
using Ae = A<int E::*, &E::e>;
using Aecb = A<int E::*, ecb>; // expected-error {{not supported}}
using Aedb = A<int E::*, edb>; // expected-error {{not supported}}
static_assert(!is_same<Ae, Aecb>, ""); // expected-error {{undeclared}} expected-error {{must be a type}}
static_assert(!is_same<Ae, Aedb>, ""); // expected-error {{undeclared}} expected-error {{must be a type}}
static_assert(!is_same<Aecb, Aedb>, ""); // expected-error 2{{undeclared}} expected-error {{must be a type}}
static_assert(is_same<Aecb, A<int E::*, (int E::*)(int C::*)&B::b>>, ""); // expected-error {{undeclared}} expected-error {{not supported}}
using Aecb = A<int E::*, ecb>;
using Aedb = A<int E::*, edb>;
static_assert(!is_same<Ae, Aecb>, "");
static_assert(!is_same<Ae, Aedb>, "");
static_assert(!is_same<Aecb, Aedb>, "");
static_assert(is_same<Aecb, A<int E::*, (int E::*)(int C::*)&B::b>>, "");

using An = A<int E::*, nullptr>;
using A0 = A<int E::*, (int E::*)0>;
Expand Down Expand Up @@ -205,9 +203,9 @@ namespace Auto {

struct Y : X {};
void type_affects_identity(B<&X::n>) {}
void type_affects_identity(B<(int Y::*)&X::n>) {} // FIXME: expected-error {{sorry}}
void type_affects_identity(B<(int Y::*)&X::n>) {}
void type_affects_identity(B<(const int X::*)&X::n>) {}
void type_affects_identity(B<(const int Y::*)&X::n>) {} // FIXME: expected-error {{sorry}}
void type_affects_identity(B<(const int Y::*)&X::n>) {}

// A case where we need to do auto-deduction, and check whether the
// resulting dependent types match during partial ordering. These
Expand Down
48 changes: 24 additions & 24 deletions clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,33 @@ namespace std {

// floating-point arguments
template<float> struct Float {};
using F1 = Float<1.0f>; // FIXME expected-error {{sorry}}
using F1 = Float<2.0f / 2>; // FIXME expected-error {{sorry}}
using F1 = Float<1.0f>;
using F1 = Float<2.0f / 2>;

struct S { int n[3]; } s; // expected-note 1+{{here}}
union U { int a, b; } u;
int n; // expected-note 1+{{here}}

// pointers to subobjects
template<int *> struct IntPtr {};
using IPn = IntPtr<&n + 1>; // FIXME expected-error {{refers to subobject}}
using IPn = IntPtr<&n + 1>; // FIXME expected-error {{refers to subobject}}
using IPn = IntPtr<&n + 1>;
using IPn = IntPtr<&n + 1>;

using IP2 = IntPtr<&s.n[2]>; // FIXME expected-error {{refers to subobject}}
using IP2 = IntPtr<s.n + 2>; // FIXME expected-error {{refers to subobject}}
using IP2 = IntPtr<&s.n[2]>;
using IP2 = IntPtr<s.n + 2>;

using IP3 = IntPtr<&s.n[3]>; // FIXME expected-error {{refers to subobject}}
using IP3 = IntPtr<s.n + 3>; // FIXME expected-error {{refers to subobject}}
using IP3 = IntPtr<&s.n[3]>;
using IP3 = IntPtr<s.n + 3>;

template<int &> struct IntRef {};
using IPn = IntRef<*(&n + 1)>; // expected-error {{not a constant expression}} expected-note {{dereferenced pointer past the end of 'n'}}
using IPn = IntRef<*(&n + 1)>; // expected-error {{not a constant expression}} expected-note {{dereferenced pointer past the end of 'n'}}
using IRn = IntRef<*(&n + 1)>; // expected-error {{not a constant expression}} expected-note {{dereferenced pointer past the end of 'n'}}
using IRn = IntRef<*(&n + 1)>; // expected-error {{not a constant expression}} expected-note {{dereferenced pointer past the end of 'n'}}

using IP2 = IntRef<s.n[2]>; // FIXME expected-error {{refers to subobject}}
using IP2 = IntRef<*(s.n + 2)>; // FIXME expected-error {{refers to subobject}}
using IR2 = IntRef<s.n[2]>;
using IR2 = IntRef<*(s.n + 2)>;

using IP3 = IntRef<s.n[3]>; // expected-error {{not a constant expression}} expected-note {{dereferenced pointer past the end of subobject of 's'}}
using IP3 = IntRef<*(s.n + 3)>; // expected-error {{not a constant expression}} expected-note {{dereferenced pointer past the end of subobject of 's'}}
using IR3 = IntRef<s.n[3]>; // expected-error {{not a constant expression}} expected-note {{dereferenced pointer past the end of subobject of 's'}}
using IR3 = IntRef<*(s.n + 3)>; // expected-error {{not a constant expression}} expected-note {{dereferenced pointer past the end of subobject of 's'}}

// classes
template<S> struct Struct {};
Expand All @@ -48,12 +48,12 @@ using U1 = Union<U{.b = 1}>; // expected-error {{different types}}

// miscellaneous scalar types
template<_Complex int> struct ComplexInt {};
using CI = ComplexInt<1 + 3i>; // FIXME: expected-error {{sorry}}
using CI = ComplexInt<1 + 3i>; // FIXME: expected-error {{sorry}}
using CI = ComplexInt<1 + 3i>;
using CI = ComplexInt<3i + 1>;

template<_Complex float> struct ComplexFloat {};
using CF = ComplexFloat<1.0f + 3.0fi>; // FIXME: expected-error {{sorry}}
using CF = ComplexFloat<1.0f + 3.0fi>; // FIXME: expected-error {{sorry}}
using CF = ComplexFloat<1.0f + 3.0fi>;
using CF = ComplexFloat<3.0fi + 1.0f>;

namespace ClassNTTP {
struct A { // expected-note 2{{candidate}}
Expand Down Expand Up @@ -307,11 +307,11 @@ namespace dependent {
if constexpr (N < 10)
return R<n>();
else if constexpr (N < 20)
return R<vn.v>(); // FIXME: expected-error 2{{refers to subobject}}
return R<vn.v>();
else if constexpr (N < 30)
return S<&n>();
else if constexpr (N < 40)
return S<&vn.v>(); // FIXME: expected-error 2{{refers to subobject}}
return S<&vn.v>();
else if constexpr (N < 50)
return T<V<int&>{n}>();
else if constexpr (N < 60)
Expand All @@ -322,15 +322,15 @@ namespace dependent {
return T<V<int*>{&vn.v}>();
}
template<int Base> void check() {
auto v = f<Base + 0>(); // FIXME: expected-note 2{{instantiation of}}
auto w = f<Base + 1>(); // FIXME: expected-note 2{{instantiation of}}
auto v = f<Base + 0>();
auto w = f<Base + 1>();
static_assert(!__is_same(decltype(v), decltype(w)));
static_assert(v != w);
}
template void check<0>();
template void check<10>(); // FIXME: expected-note 2{{instantiation of}}
template void check<10>();
template void check<20>();
template void check<30>(); // FIXME: expected-note 2{{instantiation of}}
template void check<30>();
template void check<40>();
template void check<50>();
template void check<60>();
Expand Down
5 changes: 5 additions & 0 deletions clang/tools/libclang/CIndex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1493,6 +1493,11 @@ bool CursorVisitor::VisitTemplateArgumentLoc(const TemplateArgumentLoc &TAL) {
return Visit(MakeCXCursor(E, StmtParent, TU, RegionOfInterest));
return false;

case TemplateArgument::UncommonValue:
if (Expr *E = TAL.getSourceUncommonValueExpression())
return Visit(MakeCXCursor(E, StmtParent, TU, RegionOfInterest));
return false;

case TemplateArgument::NullPtr:
if (Expr *E = TAL.getSourceNullPtrExpression())
return Visit(MakeCXCursor(E, StmtParent, TU, RegionOfInterest));
Expand Down
3 changes: 3 additions & 0 deletions clang/tools/libclang/CXCursor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1375,6 +1375,9 @@ enum CXTemplateArgumentKind clang_Cursor_getTemplateArgumentKind(CXCursor C,
return CXTemplateArgumentKind_NullPtr;
case TemplateArgument::Integral:
return CXTemplateArgumentKind_Integral;
case TemplateArgument::UncommonValue:
// FIXME: Expose these values.
return CXTemplateArgumentKind_Invalid;
case TemplateArgument::Template:
return CXTemplateArgumentKind_Template;
case TemplateArgument::TemplateExpansion:
Expand Down