Mordante updated this revision to Diff 215425.
Mordante added a comment.

Moved the testing from Parse to Sema.
Added additional safeguards for template instantiation.
Added more unit tests.
The comments may be a bit noisy, but they explain why the templates need to be 
tested at two locations.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D64811/new/

https://reviews.llvm.org/D64811

Files:
  clang/include/clang/AST/Type.h
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/include/clang/Sema/Sema.h
  clang/lib/Parse/ParseDecl.cpp
  clang/lib/Parse/ParseExprCXX.cpp
  clang/lib/Sema/SemaDeclCXX.cpp
  clang/lib/Sema/SemaOverload.cpp
  clang/lib/Sema/SemaType.cpp
  clang/test/Sema/function_parameter_overflow.cpp
  clang/test/Sema/lambda_function_parameter_overflow.cpp
  clang/test/Sema/parameter_overflow.h
  clang/test/Sema/template_function_parameter_overflow.cpp
  clang/test/Sema/variadic_function_instantiation_parameter_overflow.cpp
  clang/test/Sema/variadic_function_parameter_overflow.cpp
  clang/test/Sema/variadic_template_function_parameter_overflow.cpp

Index: clang/test/Sema/variadic_template_function_parameter_overflow.cpp
===================================================================
--- /dev/null
+++ clang/test/Sema/variadic_template_function_parameter_overflow.cpp
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 %s -fsyntax-only
+// RUN: not %clang_cc1 %s -fsyntax-only -DFAIL 2>&1 | FileCheck %s
+
+#define ARG 42
+#include "parameter_overflow.h"
+
+template <class... P>
+void foo(P &&... p);
+
+void bar() {
+  foo(A65535
+#ifdef FAIL
+      , ARG
+#endif
+  );
+}
+
+// CHECK: fatal error: number of function parameters exceeded maximum of 65535
Index: clang/test/Sema/variadic_function_parameter_overflow.cpp
===================================================================
--- /dev/null
+++ clang/test/Sema/variadic_function_parameter_overflow.cpp
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 %s -fsyntax-only
+// RUN: %clang_cc1 %s -fsyntax-only -DFAIL 2>&1
+
+#define ARG 42
+#include "parameter_overflow.h"
+
+// Unlike the other parameter overflow tests this one is not limited by
+// NumParamsBits so the test does not generate an error.
+
+void foo(...);
+
+void bar() {
+  foo(A65535
+#ifdef FAIL
+      , ARG
+#endif
+  );
+}
Index: clang/test/Sema/variadic_function_instantiation_parameter_overflow.cpp
===================================================================
--- /dev/null
+++ clang/test/Sema/variadic_function_instantiation_parameter_overflow.cpp
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 %s -fsyntax-only
+// RUN: %clang_cc1 %s -fsyntax-only -verify -DFAIL
+
+#define ARG int
+#include "parameter_overflow.h"
+
+// The expansion of T... and the existance of fp cause the overflow.
+
+void foo(A65534
+#ifdef FAIL
+  , ARG
+#endif
+);
+
+template <class... T> void foobar(void (*fp)(T...)); // expected-note {{number of function parameters exceeded maximum of 65535}}
+
+void bar() {
+  foobar<A65534 // expected-error {{no matching function for call to 'foobar'}}
+#ifdef FAIL
+  , ARG
+#endif
+  >(foo);
+}
Index: clang/test/Sema/template_function_parameter_overflow.cpp
===================================================================
--- /dev/null
+++ clang/test/Sema/template_function_parameter_overflow.cpp
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 %s -fsyntax-only
+// RUN: not %clang_cc1 %s -fsyntax-only -DFAIL 2>&1 | FileCheck %s
+
+#define ARG INT
+#include "parameter_overflow.h"
+
+template <class ARG>
+void foo(A65535
+#ifdef FAIL
+, ARG
+#endif
+);
+// CHECK: fatal error: number of function parameters exceeded maximum of 65535
Index: clang/test/Sema/parameter_overflow.h
===================================================================
--- /dev/null
+++ clang/test/Sema/parameter_overflow.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#define A10 ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG
+#define A50 A10, A10, A10, A10, A10
+#define A500 A50, A50, A50, A50, A50, A50, A50, A50, A50, A50
+#define A5000 A500, A500, A500, A500, A500, A500, A500, A500, A500, A500
+#define A60000 A5000, A5000, A5000, A5000, A5000, A5000, A5000, A5000, A5000, A5000, A5000, A5000
+
+#define  A65534 A60000, A5000, A500, A10, A10, A10, ARG, ARG, ARG, ARG
+#define  A65535 A65534, ARG
Index: clang/test/Sema/lambda_function_parameter_overflow.cpp
===================================================================
--- /dev/null
+++ clang/test/Sema/lambda_function_parameter_overflow.cpp
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 %s -fsyntax-only
+// RUN: not %clang_cc1 %s -fsyntax-only -DFAIL 2>&1 | FileCheck %s
+
+#define ARG int
+#include "parameter_overflow.h"
+
+auto foo = [](A65535
+#ifdef FAIL
+, ARG
+#endif
+){};
+// CHECK: fatal error: number of function parameters exceeded maximum of 65535
Index: clang/test/Sema/function_parameter_overflow.cpp
===================================================================
--- /dev/null
+++ clang/test/Sema/function_parameter_overflow.cpp
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 %s -fsyntax-only
+// RUN: not %clang_cc1 %s -fsyntax-only -DFAIL 2>&1 | FileCheck %s
+
+#define ARG int
+#include "parameter_overflow.h"
+
+void foo(A65535
+#ifdef FAIL
+, ARG
+#endif
+);
+// CHECK: fatal error: number of function parameters exceeded maximum of 65535
Index: clang/lib/Sema/SemaType.cpp
===================================================================
--- clang/lib/Sema/SemaType.cpp
+++ clang/lib/Sema/SemaType.cpp
@@ -2528,6 +2528,19 @@
 
   Invalid |= CheckFunctionReturnType(T, Loc);
 
+  // Variadic templates can expand to more parameters than expected.
+  // template <class... T> void foobar(void (*fp)(T...)); called with 1
+  // parameter will expand to 2 parameters at the caller side. This will be
+  // discovered upon instantiation. So we need to validate the number of
+  // parameters again. The output of this diagnostic is less clear than the one
+  // in Sema::AddTemplateOverloadCandidate.
+  if (ParamTypes.size() > FunctionType::getMaxNumParams()) {
+    Diag(Loc,
+         diag::err_number_of_function_parameters_exceeded)
+        << FunctionType::getMaxNumParams();
+    Invalid = true;
+  }
+
   for (unsigned Idx = 0, Cnt = ParamTypes.size(); Idx < Cnt; ++Idx) {
     // FIXME: Loc is too inprecise here, should use proper locations for args.
     QualType ParamType = Context.getAdjustedParameterType(ParamTypes[Idx]);
Index: clang/lib/Sema/SemaOverload.cpp
===================================================================
--- clang/lib/Sema/SemaOverload.cpp
+++ clang/lib/Sema/SemaOverload.cpp
@@ -6783,6 +6783,16 @@
   if (!CandidateSet.isNewCandidate(FunctionTemplate))
     return;
 
+  // Test for variadic templates candidates that cannot be called due to too
+  // many arguments. Warn here instead of at the call site to avoid printing the
+  // candidate that never can be called.
+  if (Args.size() > FunctionType::getMaxNumParams()) {
+    Diag(CandidateSet.getLocation(),
+         diag::err_number_of_function_parameters_exceeded)
+        << FunctionType::getMaxNumParams();
+    return;
+  }
+
   // C++ [over.match.funcs]p7:
   //   In each case where a candidate is a function template, candidate
   //   function template specializations are generated using template argument
Index: clang/lib/Sema/SemaDeclCXX.cpp
===================================================================
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -291,6 +291,22 @@
   return false;
 }
 
+/// Check whether the number of parameters in a function declarations is below
+/// the maximum allowed.
+bool
+Sema::ActOnParamInfo(
+    SourceLocation Loc,
+    const SmallVectorImpl<DeclaratorChunk::ParamInfo> &ParamInfo) {
+
+  if (ParamInfo.size() <= FunctionType::getMaxNumParams())
+    return false;
+
+  Diag(ParamInfo[FunctionType::getMaxNumParams() - 1].IdentLoc,
+       diag::err_number_of_function_parameters_exceeded)
+      << FunctionType::getMaxNumParams();
+  return true;
+}
+
 /// ActOnParamDefaultArgument - Check whether the default argument
 /// provided for a function parameter is well-formed. If so, attach it
 /// to the parameter declaration.
Index: clang/lib/Parse/ParseExprCXX.cpp
===================================================================
--- clang/lib/Parse/ParseExprCXX.cpp
+++ clang/lib/Parse/ParseExprCXX.cpp
@@ -1264,6 +1264,8 @@
           CurTemplateDepthTracker.getOriginalDepth());
 
       ParseParameterDeclarationClause(D, Attr, ParamInfo, EllipsisLoc);
+      if (Actions.ActOnParamInfo(DeclLoc, ParamInfo))
+        return ExprError();
 
       // For a generic lambda, each 'auto' within the parameter declaration
       // clause creates a template type parameter, so increment the depth.
Index: clang/lib/Parse/ParseDecl.cpp
===================================================================
--- clang/lib/Parse/ParseDecl.cpp
+++ clang/lib/Parse/ParseDecl.cpp
@@ -6224,10 +6224,13 @@
     MaybeParseCXX11Attributes(FnAttrs);
     ProhibitAttributes(FnAttrs);
   } else {
-    if (Tok.isNot(tok::r_paren))
+    if (Tok.isNot(tok::r_paren)) {
       ParseParameterDeclarationClause(D, FirstArgAttrs, ParamInfo,
                                       EllipsisLoc);
-    else if (RequiresArg)
+      if (Actions.ActOnParamInfo(StartLoc, ParamInfo))
+        return;
+
+  } else if (RequiresArg)
       Diag(Tok, diag::err_argument_required_after_attribute);
 
     HasProto = ParamInfo.size() || getLangOpts().CPlusPlus
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -2114,6 +2114,10 @@
   bool SetParamDefaultArgument(ParmVarDecl *Param, Expr *DefaultArg,
                                SourceLocation EqualLoc);
 
+  bool
+  ActOnParamInfo(SourceLocation Loc,
+                 const SmallVectorImpl<DeclaratorChunk::ParamInfo> &ParamInfo);
+
   // Contexts where using non-trivial C union types can be disallowed. This is
   // passed to err_non_trivial_c_union_in_invalid_context.
   enum NonTrivialCUnionContext {
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -248,6 +248,8 @@
   "redeclaration of %0 with a different type%diff{: $ vs $|}1,2">;
 def err_bad_variable_name : Error<
   "%0 cannot be the name of a variable or data member">;
+def err_number_of_function_parameters_exceeded : Error<
+  "number of function parameters exceeded maximum of %0">, DefaultFatal;
 def err_bad_parameter_name : Error<
   "%0 cannot be the name of a parameter">;
 def err_bad_parameter_name_template_id : Error<
Index: clang/include/clang/AST/Type.h
===================================================================
--- clang/include/clang/AST/Type.h
+++ clang/include/clang/AST/Type.h
@@ -1527,6 +1527,8 @@
     friend class FunctionProtoType;
     friend class FunctionType;
 
+    enum { NumParamsBits = 16 };
+
     unsigned : NumTypeBits;
 
     /// Extra information which affects how the function is called, like
@@ -1552,7 +1554,7 @@
     /// According to [implimits] 8 bits should be enough here but this is
     /// somewhat easy to exceed with metaprogramming and so we would like to
     /// keep NumParams as wide as reasonably possible.
-    unsigned NumParams : 16;
+    unsigned NumParams : NumParamsBits;
 
     /// The type of exception specification this function has.
     unsigned ExceptionSpecType : 4;
@@ -3642,6 +3644,11 @@
   }
 
 public:
+  /// @returns The maximum number of parameters for a function.
+  static constexpr unsigned getMaxNumParams() {
+    return (1u << FunctionTypeBitfields::NumParamsBits) - 1;
+  }
+
   QualType getReturnType() const { return ResultType; }
 
   bool getHasRegParm() const { return getExtInfo().getHasRegParm(); }
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D64811: Warn when N... Mark de Wever via Phabricator via cfe-commits

Reply via email to