diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index e800f7fe742482..85240e1aeec8ab 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -6968,100 +6968,77 @@ Sema::BuildExpressionFromDeclTemplateArgument(const TemplateArgument &Arg, ValueDecl *VD = Arg.getAsDecl(); - if (VD->getDeclContext()->isRecord() && - (isa(VD) || isa(VD) || - isa(VD))) { - // If the value is a class member, we might have a pointer-to-member. - // Determine whether the non-type template template parameter is of - // pointer-to-member type. If so, we need to build an appropriate - // expression for a pointer-to-member, since a "normal" DeclRefExpr - // would refer to the member itself. - if (ParamType->isMemberPointerType()) { - QualType ClassType - = Context.getTypeDeclType(cast(VD->getDeclContext())); - NestedNameSpecifier *Qualifier - = NestedNameSpecifier::Create(Context, nullptr, false, - ClassType.getTypePtr()); - CXXScopeSpec SS; - SS.MakeTrivial(Context, Qualifier, Loc); - - // The actual value-ness of this is unimportant, but for - // internal consistency's sake, references to instance methods - // are r-values. - ExprValueKind VK = VK_LValue; - if (isa(VD) && cast(VD)->isInstance()) - VK = VK_RValue; - - ExprResult RefExpr = BuildDeclRefExpr(VD, - VD->getType().getNonReferenceType(), - VK, - Loc, - &SS); - if (RefExpr.isInvalid()) - return ExprError(); - - RefExpr = CreateBuiltinUnaryOp(Loc, UO_AddrOf, RefExpr.get()); - - // We might need to perform a trailing qualification conversion, since - // the element type on the parameter could be more qualified than the - // element type in the expression we constructed, and likewise for a - // function conversion. - bool ObjCLifetimeConversion; - QualType Ignored; - if (IsFunctionConversion(RefExpr.get()->getType(), ParamType, Ignored) || - IsQualificationConversion(RefExpr.get()->getType(), - ParamType.getUnqualifiedType(), false, - ObjCLifetimeConversion)) - RefExpr = ImpCastExprToType(RefExpr.get(), - ParamType.getUnqualifiedType(), CK_NoOp); - - // FIXME: We need to perform derived-to-base or base-to-derived - // pointer-to-member conversions here too. - assert(!RefExpr.isInvalid() && - Context.hasSameType(RefExpr.get()->getType(), - ParamType.getUnqualifiedType())); - return RefExpr; - } - } - - QualType T = VD->getType().getNonReferenceType(); + CXXScopeSpec SS; + if (ParamType->isMemberPointerType()) { + // If this is a pointer to member, we need to use a qualified name to + // form a suitable pointer-to-member constant. + assert(VD->getDeclContext()->isRecord() && + (isa(VD) || isa(VD) || + isa(VD))); + QualType ClassType + = Context.getTypeDeclType(cast(VD->getDeclContext())); + NestedNameSpecifier *Qualifier + = NestedNameSpecifier::Create(Context, nullptr, false, + ClassType.getTypePtr()); + SS.MakeTrivial(Context, Qualifier, Loc); + } + + ExprResult RefExpr = BuildDeclarationNameExpr( + SS, DeclarationNameInfo(VD->getDeclName(), Loc), VD); + if (RefExpr.isInvalid()) + return ExprError(); - if (ParamType->isPointerType()) { - // When the non-type template parameter is a pointer, take the - // address of the declaration. - ExprResult RefExpr = BuildDeclRefExpr(VD, T, VK_LValue, Loc); + // For a pointer, the argument declaration is the pointee. Take its address. + QualType T = RefExpr.get()->getType(); + bool ObjCLifetimeConversion; + if (ParamType->isPointerType() && + (T->isFunctionType() || + (T->isArrayType() && + !Context.hasSameUnqualifiedType(T, ParamType->getPointeeType()) && + !IsQualificationConversion(T, ParamType->getPointeeType(), false, + ObjCLifetimeConversion)))) { + // Decay functions and arrays unless we're forming a pointer to array. + RefExpr = DefaultFunctionArrayConversion(RefExpr.get()); if (RefExpr.isInvalid()) return ExprError(); - - if (!Context.hasSameUnqualifiedType(ParamType->getPointeeType(), T) && - (T->isFunctionType() || T->isArrayType())) { - // Decay functions and arrays unless we're forming a pointer to array. - RefExpr = DefaultFunctionArrayConversion(RefExpr.get()); - if (RefExpr.isInvalid()) - return ExprError(); - - return RefExpr; + } else if (ParamType->isPointerType() || ParamType->isMemberPointerType()) { + RefExpr = CreateBuiltinUnaryOp(Loc, UO_AddrOf, RefExpr.get()); + if (RefExpr.isInvalid()) + return ExprError(); + } else { + assert(ParamType->isReferenceType() && + "unexpected type for decl template argument"); + } + + // At this point we should have the right value category. + assert(ParamType->isReferenceType() == RefExpr.get()->isLValue() && + "value kind mismatch for non-type template argument"); + + // The type of the template parameter can differ from the type of the + // argument in various ways; convert it now if necessary. + QualType DestExprType = ParamType.getNonLValueExprType(Context); + if (!Context.hasSameType(RefExpr.get()->getType(), DestExprType)) { + CastKind CK; + QualType Ignored; + if (Context.hasSimilarType(RefExpr.get()->getType(), DestExprType) || + IsFunctionConversion(RefExpr.get()->getType(), DestExprType, Ignored)) { + CK = CK_NoOp; + } else if (ParamType->isVoidPointerType() && + RefExpr.get()->getType()->isPointerType()) { + CK = CK_BitCast; + } else { + // FIXME: Pointers to members can need conversion derived-to-base or + // base-to-derived conversions. We currently don't retain enough + // information to convert properly (we need to track a cast path or + // subobject number in the template argument). + llvm_unreachable( + "unexpected conversion required for non-type template argument"); } - - // Take the address of everything else - return CreateBuiltinUnaryOp(Loc, UO_AddrOf, RefExpr.get()); - } - - ExprValueKind VK = VK_RValue; - - // If the non-type template parameter has reference type, qualify the - // resulting declaration reference with the extra qualifiers on the - // type that the reference refers to. - if (const ReferenceType *TargetRef = ParamType->getAs()) { - VK = VK_LValue; - T = Context.getQualifiedType(T, - TargetRef->getPointeeType().getQualifiers()); - } else if (isa(VD)) { - // References to functions are always lvalues. - VK = VK_LValue; + RefExpr = ImpCastExprToType(RefExpr.get(), DestExprType, CK, + RefExpr.get()->getValueKind()); } - return BuildDeclRefExpr(VD, T, VK, Loc); + return RefExpr; } /// Construct a new expression that refers to the given diff --git a/clang/test/SemaCXX/exceptions-seh.cpp b/clang/test/SemaCXX/exceptions-seh.cpp index 1d8cc4917e9894..02bb786160dcfe 100644 --- a/clang/test/SemaCXX/exceptions-seh.cpp +++ b/clang/test/SemaCXX/exceptions-seh.cpp @@ -39,14 +39,13 @@ void instantiate_bad_scope_tmpl() { } #if __cplusplus < 201103L -// FIXME: Diagnose this case. For now we produce undef in codegen. template T func_template() { - return FN(); + return FN(); // expected-error 2{{builtin functions must be directly called}} } void inject_builtins() { - func_template(); - func_template(); + func_template(); // expected-note {{instantiation of}} + func_template(); // expected-note {{instantiation of}} } #endif diff --git a/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp b/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp index 7a58dd5dcaeda0..7232598215acc3 100644 --- a/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp +++ b/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp @@ -394,6 +394,21 @@ namespace PR42362 { Z::Q q; } +namespace QualConv { + int *X; + template void f() { + using T = decltype(P); + using T = const int* const*; + } + template void f<&X>(); + + template void g() { + using T = decltype(R); + using T = const int *const &; + } + template void g<(const int *const&)X>(); +} + namespace FunctionConversion { struct a { void c(char *) noexcept; }; template void g() { @@ -401,4 +416,21 @@ namespace FunctionConversion { using T = void (a::*)(char*); // (not 'noexcept') } template void g<&a::c>(); + + void c() noexcept; + template void h() { + using T = decltype(p); + using T = void (*)(); // (not 'noexcept') + } + template void h<&c>(); +} + +namespace VoidPtr { + // Note, this is an extension in C++17 but valid in C++20. + template void f() { + using T = decltype(P); + using T = void*; + } + int n; + template void f<(void*)&n>(); }