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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits