r189087 - Sema: Properly support Microsoft-mode template arguments
David Majnemer
david.majnemer at gmail.com
Thu Aug 22 22:39:40 PDT 2013
Author: majnemer
Date: Fri Aug 23 00:39:39 2013
New Revision: 189087
URL: http://llvm.org/viewvc/llvm-project?rev=189087&view=rev
Log:
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
Modified:
cfe/trunk/include/clang/AST/TemplateBase.h
cfe/trunk/lib/Sema/SemaTemplate.cpp
cfe/trunk/test/Parser/MicrosoftExtensions.cpp
Modified: cfe/trunk/include/clang/AST/TemplateBase.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/TemplateBase.h?rev=189087&r1=189086&r2=189087&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/TemplateBase.h (original)
+++ cfe/trunk/include/clang/AST/TemplateBase.h Fri Aug 23 00:39:39 2013
@@ -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.
Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=189087&r1=189086&r2=189087&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Fri Aug 23 00:39:39 2013
@@ -4072,6 +4072,63 @@ isNullPointerValueTemplateArgument(Sema
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<PointerType>()->getPointeeType()->isFunctionType() &&
+ S.IsQualificationConversion(ArgType, ParamType, false,
+ ObjCLifetimeConversion)) {
+ // For pointer-to-object types, qualification conversions are
+ // permitted.
+ } else {
+ if (const ReferenceType *ParamRef = ParamType->getAs<ReferenceType>()) {
+ 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 @@ CheckTemplateArgumentAddressOfObjectOrFu
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<ParenExpr>(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<UnaryOperator>(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<ParenExpr>(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<SubstNonTypeTemplateParmExpr>(Arg))
- Arg = subst->getReplacement()->IgnoreImpCasts();
+ Arg = Parens->getSubExpr();
+ }
- bool AddressTaken = false;
- SourceLocation AddrOpLoc;
- if (UnaryOperator *UnOp = dyn_cast<UnaryOperator>(Arg)) {
- if (UnOp->getOpcode() == UO_AddrOf) {
- Arg = UnOp->getSubExpr();
- AddressTaken = true;
- AddrOpLoc = UnOp->getOperatorLoc();
+ while (SubstNonTypeTemplateParmExpr *subst =
+ dyn_cast<SubstNonTypeTemplateParmExpr>(Arg))
+ Arg = subst->getReplacement()->IgnoreImpCasts();
+
+ if (UnaryOperator *UnOp = dyn_cast<UnaryOperator>(Arg)) {
+ if (UnOp->getOpcode() == UO_AddrOf) {
+ Arg = UnOp->getSubExpr();
+ AddressTaken = true;
+ AddrOpLoc = UnOp->getOperatorLoc();
+ }
}
+
+ while (SubstNonTypeTemplateParmExpr *subst =
+ dyn_cast<SubstNonTypeTemplateParmExpr>(Arg))
+ Arg = subst->getReplacement()->IgnoreImpCasts();
}
- if (isa<CXXUuidofExpr>(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<SubstNonTypeTemplateParmExpr>(Arg))
- Arg = subst->getReplacement()->IgnoreImpCasts();
+ if (isa<CXXUuidofExpr>(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<DeclRefExpr>(Arg);
if (!DRE) {
S.Diag(Arg->getLocStart(), diag::err_template_arg_not_decl_ref)
@@ -4305,55 +4404,9 @@ CheckTemplateArgumentAddressOfObjectOrFu
}
}
- bool ObjCLifetimeConversion;
- if (ParamType->isPointerType() &&
- !ParamType->getAs<PointerType>()->getPointeeType()->isFunctionType() &&
- S.IsQualificationConversion(ArgType, ParamType, false,
- ObjCLifetimeConversion)) {
- // For pointer-to-object types, qualification conversions are
- // permitted.
- } else {
- if (const ReferenceType *ParamRef = ParamType->getAs<ReferenceType>()) {
- 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<ValueDecl>(Entity->getCanonicalDecl()),
Modified: cfe/trunk/test/Parser/MicrosoftExtensions.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/MicrosoftExtensions.cpp?rev=189087&r1=189086&r2=189087&view=diff
==============================================================================
--- cfe/trunk/test/Parser/MicrosoftExtensions.cpp (original)
+++ cfe/trunk/test/Parser/MicrosoftExtensions.cpp Fri Aug 23 00:39:39 2013
@@ -95,10 +95,10 @@ void template_uuid()
}
-template <class T, const GUID* g = &__uuidof(T)>
+template <class T, const GUID* g = &__uuidof(T)> // expected-note {{template parameter is declared here}}
class COM_CLASS_TEMPLATE { };
-typedef COM_CLASS_TEMPLATE<struct_with_uuid, &__uuidof(struct_with_uuid)> COM_TYPE_1;
+typedef COM_CLASS_TEMPLATE<struct_with_uuid, &*&__uuidof(struct_with_uuid)> COM_TYPE_1; // expected-warning {{non-type template argument containing a dereference operation is a Microsoft extension}}
typedef COM_CLASS_TEMPLATE<struct_with_uuid> COM_TYPE_2;
template <class T, const GUID& g>
@@ -112,6 +112,9 @@ typedef COM_CLASS_TEMPLATE_REF<struct_wi
}
struct __declspec(uuid("000000A0-0000-0000-C000-000000000049")) late_defined_uuid;
+COM_CLASS_TEMPLATE_REF<int, __uuidof(struct_with_uuid)> good_template_arg;
+
+COM_CLASS_TEMPLATE<int, __uuidof(struct_with_uuid)> bad_template_arg; // expected-error {{non-type template argument of type 'const _GUID' is not a constant expression}}
class CtorCall {
public:
More information about the cfe-commits
mailing list