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(); } 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/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 &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/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/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/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 &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/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/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 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/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/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/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/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 +void foo(A65535 +#ifdef FAIL +, ARG +#endif +); +// CHECK: fatal error: number of function parameters exceeded maximum of 65535 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 void foobar(void (*fp)(T...)); // expected-note {{number of function parameters exceeded maximum of 65535}} + +void bar() { + foobar(foo); +} 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_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 +void foo(P &&... p); + +void bar() { + foo(A65535 +#ifdef FAIL + , ARG +#endif + ); +} + +// CHECK: fatal error: number of function parameters exceeded maximum of 65535