From: David Majnemer Date: Fri, 23 Aug 2013 05:39:39 +0000 (+0000) Subject: Sema: Properly support Microsoft-mode template arguments X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d1282ec56231c967439c1eb67fe4afe792287911;p=clang Sema: Properly support Microsoft-mode template arguments Summary: There were two things known to be wrong with our implementation of MSVC mode template arguments: - We didn't properly handle __uuidof/CXXUuidofExpr and skipped all type checking completely. - We didn't allow for MSVC's extension of allowing certain constant "foldable" expressions from showing up in template arguments. They allow various casts dereference and address-of operations. We can make it more general as we find further peculiarities but this is the known extent. Reviewers: rsmith, doug.gregor, rjmccall Reviewed By: doug.gregor CC: cfe-commits, rnk Differential Revision: http://llvm-reviews.chandlerc.com/D1444 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@189087 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/TemplateBase.h b/include/clang/AST/TemplateBase.h index 490a83743a..6c40eb1168 100644 --- a/include/clang/AST/TemplateBase.h +++ b/include/clang/AST/TemplateBase.h @@ -60,8 +60,8 @@ public: /// The template argument is a pack expansion of a template name that was /// provided for a template template parameter. TemplateExpansion, - /// The template argument is a value- or type-dependent expression - /// stored in an Expr*. + /// The template argument is a value- or type-dependent expression or a + /// non-dependent __uuidof expression stored in an Expr*. Expression, /// The template argument is actually a parameter pack. Arguments are stored /// in the Args struct. diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 01a42e3b99..a6a1ea4698 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -4072,6 +4072,63 @@ isNullPointerValueTemplateArgument(Sema &S, NonTypeTemplateParmDecl *Param, return NPV_NotNullPointer; } +/// \brief Checks whether the given template argument is compatible with its +/// template parameter. +static bool CheckTemplateArgumentIsCompatibleWithParameter( + Sema &S, NonTypeTemplateParmDecl *Param, QualType ParamType, Expr *ArgIn, + Expr *Arg, QualType ArgType) { + bool ObjCLifetimeConversion; + if (ParamType->isPointerType() && + !ParamType->getAs()->getPointeeType()->isFunctionType() && + S.IsQualificationConversion(ArgType, ParamType, false, + ObjCLifetimeConversion)) { + // For pointer-to-object types, qualification conversions are + // permitted. + } else { + if (const ReferenceType *ParamRef = ParamType->getAs()) { + if (!ParamRef->getPointeeType()->isFunctionType()) { + // C++ [temp.arg.nontype]p5b3: + // For a non-type template-parameter of type reference to + // object, no conversions apply. The type referred to by the + // reference may be more cv-qualified than the (otherwise + // identical) type of the template- argument. The + // template-parameter is bound directly to the + // template-argument, which shall be an lvalue. + + // FIXME: Other qualifiers? + unsigned ParamQuals = ParamRef->getPointeeType().getCVRQualifiers(); + unsigned ArgQuals = ArgType.getCVRQualifiers(); + + if ((ParamQuals | ArgQuals) != ParamQuals) { + S.Diag(Arg->getLocStart(), + diag::err_template_arg_ref_bind_ignores_quals) + << ParamType << Arg->getType() << Arg->getSourceRange(); + S.Diag(Param->getLocation(), diag::note_template_param_here); + return true; + } + } + } + + // At this point, the template argument refers to an object or + // function with external linkage. We now need to check whether the + // argument and parameter types are compatible. + if (!S.Context.hasSameUnqualifiedType(ArgType, + ParamType.getNonReferenceType())) { + // We can't perform this conversion or binding. + if (ParamType->isReferenceType()) + S.Diag(Arg->getLocStart(), diag::err_template_arg_no_ref_bind) + << ParamType << ArgIn->getType() << Arg->getSourceRange(); + else + S.Diag(Arg->getLocStart(), diag::err_template_arg_not_convertible) + << ArgIn->getType() << ParamType << Arg->getSourceRange(); + S.Diag(Param->getLocation(), diag::note_template_param_here); + return true; + } + } + + return false; +} + /// \brief Checks whether the given template argument is the address /// of an object or function according to C++ [temp.arg.nontype]p1. static bool @@ -4099,68 +4156,110 @@ CheckTemplateArgumentAddressOfObjectOrFunction(Sema &S, break; } } - - // See through any implicit casts we added to fix the type. - Arg = Arg->IgnoreImpCasts(); - - // C++ [temp.arg.nontype]p1: - // - // A template-argument for a non-type, non-template - // template-parameter shall be one of: [...] - // - // -- the address of an object or function with external - // linkage, including function templates and function - // template-ids but excluding non-static class members, - // expressed as & id-expression where the & is optional if - // the name refers to a function or array, or if the - // corresponding template-parameter is a reference; or - // In C++98/03 mode, give an extension warning on any extra parentheses. - // See http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#773 - bool ExtraParens = false; - while (ParenExpr *Parens = dyn_cast(Arg)) { - if (!Invalid && !ExtraParens) { - S.Diag(Arg->getLocStart(), - S.getLangOpts().CPlusPlus11 ? - diag::warn_cxx98_compat_template_arg_extra_parens : - diag::ext_template_arg_extra_parens) - << Arg->getSourceRange(); - ExtraParens = true; + bool AddressTaken = false; + SourceLocation AddrOpLoc; + if (S.getLangOpts().MicrosoftExt) { + // Microsoft Visual C++ strips all casts, allows an arbitrary number of + // dereference and address-of operators. + Arg = Arg->IgnoreParenCasts(); + + bool ExtWarnMSTemplateArg = false; + UnaryOperatorKind FirstOpKind; + SourceLocation FirstOpLoc; + while (UnaryOperator *UnOp = dyn_cast(Arg)) { + UnaryOperatorKind UnOpKind = UnOp->getOpcode(); + if (UnOpKind == UO_Deref) + ExtWarnMSTemplateArg = true; + if (UnOpKind == UO_AddrOf || UnOpKind == UO_Deref) { + Arg = UnOp->getSubExpr()->IgnoreParenCasts(); + if (!AddrOpLoc.isValid()) { + FirstOpKind = UnOpKind; + FirstOpLoc = UnOp->getOperatorLoc(); + } + } else + break; } + if (FirstOpLoc.isValid()) { + if (ExtWarnMSTemplateArg) + S.Diag(ArgIn->getLocStart(), diag::ext_ms_deref_template_argument) + << ArgIn->getSourceRange(); + + if (FirstOpKind == UO_AddrOf) + AddressTaken = true; + else if (Arg->getType()->isPointerType()) { + // We cannot let pointers get dereferenced here, that is obviously not a + // constant expression. + assert(FirstOpKind == UO_Deref); + S.Diag(Arg->getLocStart(), diag::err_template_arg_not_decl_ref) + << Arg->getSourceRange(); + } + } + } else { + // See through any implicit casts we added to fix the type. + Arg = Arg->IgnoreImpCasts(); - Arg = Parens->getSubExpr(); - } + // C++ [temp.arg.nontype]p1: + // + // A template-argument for a non-type, non-template + // template-parameter shall be one of: [...] + // + // -- the address of an object or function with external + // linkage, including function templates and function + // template-ids but excluding non-static class members, + // expressed as & id-expression where the & is optional if + // the name refers to a function or array, or if the + // corresponding template-parameter is a reference; or + + // In C++98/03 mode, give an extension warning on any extra parentheses. + // See http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#773 + bool ExtraParens = false; + while (ParenExpr *Parens = dyn_cast(Arg)) { + if (!Invalid && !ExtraParens) { + S.Diag(Arg->getLocStart(), + S.getLangOpts().CPlusPlus11 + ? diag::warn_cxx98_compat_template_arg_extra_parens + : diag::ext_template_arg_extra_parens) + << Arg->getSourceRange(); + ExtraParens = true; + } - while (SubstNonTypeTemplateParmExpr *subst = - dyn_cast(Arg)) - Arg = subst->getReplacement()->IgnoreImpCasts(); + Arg = Parens->getSubExpr(); + } - bool AddressTaken = false; - SourceLocation AddrOpLoc; - if (UnaryOperator *UnOp = dyn_cast(Arg)) { - if (UnOp->getOpcode() == UO_AddrOf) { - Arg = UnOp->getSubExpr(); - AddressTaken = true; - AddrOpLoc = UnOp->getOperatorLoc(); + while (SubstNonTypeTemplateParmExpr *subst = + dyn_cast(Arg)) + Arg = subst->getReplacement()->IgnoreImpCasts(); + + if (UnaryOperator *UnOp = dyn_cast(Arg)) { + if (UnOp->getOpcode() == UO_AddrOf) { + Arg = UnOp->getSubExpr(); + AddressTaken = true; + AddrOpLoc = UnOp->getOperatorLoc(); + } } + + while (SubstNonTypeTemplateParmExpr *subst = + dyn_cast(Arg)) + Arg = subst->getReplacement()->IgnoreImpCasts(); } - if (isa(Arg)) { + // Stop checking the precise nature of the argument if it is value dependent, + // it should be checked when instantiated. + if (Arg->isValueDependent()) { Converted = TemplateArgument(ArgIn); return false; } - while (SubstNonTypeTemplateParmExpr *subst = - dyn_cast(Arg)) - Arg = subst->getReplacement()->IgnoreImpCasts(); + if (isa(Arg)) { + if (CheckTemplateArgumentIsCompatibleWithParameter(S, Param, ParamType, + ArgIn, Arg, ArgType)) + return true; - // Stop checking the precise nature of the argument if it is value dependent, - // it should be checked when instantiated. - if (Arg->isValueDependent()) { Converted = TemplateArgument(ArgIn); return false; } - + DeclRefExpr *DRE = dyn_cast(Arg); if (!DRE) { S.Diag(Arg->getLocStart(), diag::err_template_arg_not_decl_ref) @@ -4305,55 +4404,9 @@ CheckTemplateArgumentAddressOfObjectOrFunction(Sema &S, } } - bool ObjCLifetimeConversion; - if (ParamType->isPointerType() && - !ParamType->getAs()->getPointeeType()->isFunctionType() && - S.IsQualificationConversion(ArgType, ParamType, false, - ObjCLifetimeConversion)) { - // For pointer-to-object types, qualification conversions are - // permitted. - } else { - if (const ReferenceType *ParamRef = ParamType->getAs()) { - if (!ParamRef->getPointeeType()->isFunctionType()) { - // C++ [temp.arg.nontype]p5b3: - // For a non-type template-parameter of type reference to - // object, no conversions apply. The type referred to by the - // reference may be more cv-qualified than the (otherwise - // identical) type of the template- argument. The - // template-parameter is bound directly to the - // template-argument, which shall be an lvalue. - - // FIXME: Other qualifiers? - unsigned ParamQuals = ParamRef->getPointeeType().getCVRQualifiers(); - unsigned ArgQuals = ArgType.getCVRQualifiers(); - - if ((ParamQuals | ArgQuals) != ParamQuals) { - S.Diag(Arg->getLocStart(), - diag::err_template_arg_ref_bind_ignores_quals) - << ParamType << Arg->getType() - << Arg->getSourceRange(); - S.Diag(Param->getLocation(), diag::note_template_param_here); - return true; - } - } - } - - // At this point, the template argument refers to an object or - // function with external linkage. We now need to check whether the - // argument and parameter types are compatible. - if (!S.Context.hasSameUnqualifiedType(ArgType, - ParamType.getNonReferenceType())) { - // We can't perform this conversion or binding. - if (ParamType->isReferenceType()) - S.Diag(Arg->getLocStart(), diag::err_template_arg_no_ref_bind) - << ParamType << ArgIn->getType() << Arg->getSourceRange(); - else - S.Diag(Arg->getLocStart(), diag::err_template_arg_not_convertible) - << ArgIn->getType() << ParamType << Arg->getSourceRange(); - S.Diag(Param->getLocation(), diag::note_template_param_here); - return true; - } - } + if (CheckTemplateArgumentIsCompatibleWithParameter(S, Param, ParamType, ArgIn, + Arg, ArgType)) + return true; // Create the template argument. Converted = TemplateArgument(cast(Entity->getCanonicalDecl()), diff --git a/test/Parser/MicrosoftExtensions.cpp b/test/Parser/MicrosoftExtensions.cpp index 3df461053d..1102fcbcec 100644 --- a/test/Parser/MicrosoftExtensions.cpp +++ b/test/Parser/MicrosoftExtensions.cpp @@ -95,10 +95,10 @@ void template_uuid() } -template +template // expected-note {{template parameter is declared here}} class COM_CLASS_TEMPLATE { }; -typedef COM_CLASS_TEMPLATE COM_TYPE_1; +typedef COM_CLASS_TEMPLATE COM_TYPE_1; // expected-warning {{non-type template argument containing a dereference operation is a Microsoft extension}} typedef COM_CLASS_TEMPLATE COM_TYPE_2; template @@ -112,6 +112,9 @@ typedef COM_CLASS_TEMPLATE_REF COM } struct __declspec(uuid("000000A0-0000-0000-C000-000000000049")) late_defined_uuid; +COM_CLASS_TEMPLATE_REF good_template_arg; + +COM_CLASS_TEMPLATE bad_template_arg; // expected-error {{non-type template argument of type 'const _GUID' is not a constant expression}} class CtorCall { public: