diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2985,6 +2985,9 @@ def warn_declspec_allocator_nonpointer : Warning< "ignoring __declspec(allocator) because the function return type %0 is not " "a pointer or reference type">, InGroup; +def err_cconv_incomplete_param_type : Error< + "parameter %0 must have a complete type to use function %1 with the %2 " + "calling convention">; def ext_cannot_use_trivial_abi : ExtWarn< "'trivial_abi' cannot be applied to %0">, InGroup; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -14793,6 +14793,74 @@ llvm_unreachable("Invalid context"); } +/// Return true if this function has a calling convention that requires mangling +/// in the size of the parameter pack. +static bool funcHasParameterSizeMangling(Sema &S, FunctionDecl *FD) { + // Do nothing on non-Windows, non-x86/x64 platforms. + const llvm::Triple &TT = S.Context.getTargetInfo().getTriple(); + if (!TT.isOSWindows() || (TT.getArch() != llvm::Triple::x86 && + TT.getArch() != llvm::Triple::x86_64)) + return false; + + // Stdcall, fastcall, and vectorcall need this special treatment. + CallingConv CC = FD->getType()->castAs()->getCallConv(); + switch (CC) { + case CC_X86StdCall: + case CC_X86FastCall: + case CC_X86VectorCall: + return true; + default: + break; + } + return false; +} + +/// Require that all of the parameter types of function be complete. Normally, +/// parameter types are only required to be complete when a function is called +/// or defined, but to mangle functions with certain calling conventions, the +/// mangler needs to know the size of the parameter list. In this situation, +/// MSVC doesn't emit an error or instantiate templates. Instead, MSVC mangles +/// the function as _foo@0, i.e. zero bytes of parameters, which will usually +/// result in a linker error. Clang doesn't implement this behavior, and instead +/// attempts to error at compile time. +static void CheckCompleteParameterTypesForMangler(Sema &S, FunctionDecl *FD, + SourceLocation Loc) { + class ParamIncompleteTypeDiagnoser : public Sema::TypeDiagnoser { + FunctionDecl *FD; + ParmVarDecl *Param; + + public: + ParamIncompleteTypeDiagnoser(FunctionDecl *FD, ParmVarDecl *Param) + : FD(FD), Param(Param) {} + + void diagnose(Sema &S, SourceLocation Loc, QualType T) override { + CallingConv CC = FD->getType()->castAs()->getCallConv(); + StringRef CCName; + switch (CC) { + case CC_X86StdCall: + CCName = "stdcall"; + break; + case CC_X86FastCall: + CCName = "fastcall"; + break; + case CC_X86VectorCall: + CCName = "vectorcall"; + break; + default: + llvm_unreachable("CC does not need mangling"); + } + + S.Diag(Loc, diag::err_cconv_incomplete_param_type) + << Param->getDeclName() << FD->getDeclName() << CCName; + } + }; + + for (ParmVarDecl *Param : FD->parameters()) { + ParamIncompleteTypeDiagnoser Diagnoser(FD, Param); + S.RequireCompleteType(Loc, Param->getType(), Diagnoser); + } +} + namespace { enum class OdrUseContext { /// Declarations in this context are not odr-used. @@ -15038,6 +15106,12 @@ UndefinedButUsed.insert(std::make_pair(Func->getCanonicalDecl(), Loc)); } + // Some x86 Windows calling conventions mangle the size of the parameter + // pack into the name. Computing the size of the parameters requires the + // parameter types to be complete. Check that now. + if (funcHasParameterSizeMangling(*this, Func)) + CheckCompleteParameterTypesForMangler(*this, Func, Loc); + Func->markUsed(Context); if (LangOpts.OpenMP && LangOpts.OpenMPIsDevice) diff --git a/clang/test/SemaCXX/calling-conv-complete-params.cpp b/clang/test/SemaCXX/calling-conv-complete-params.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/calling-conv-complete-params.cpp @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++11 -fms-extensions -verify -triple i686-pc-win32 %s +// RUN: %clang_cc1 -fsyntax-only -std=c++11 -fms-extensions -verify -triple x86_64-pc-win32 %s + +struct Foo; // expected-note 1+ {{forward declaration of 'Foo'}} + +void __stdcall fwd_std(struct Foo p); +#ifdef _M_IX86 +// expected-error@+2 {{parameter 'p' must have a complete type to use function 'fwd_std' with the stdcall calling convention}} +#endif +void (__stdcall *fp_fwd_std)(struct Foo) = &fwd_std; + +void __fastcall fwd_fast(struct Foo p); +#ifdef _M_IX86 +// expected-error@+2 {{parameter 'p' must have a complete type to use function 'fwd_fast' with the fastcall calling convention}} +#endif +void (__fastcall *fp_fwd_fast)(struct Foo) = &fwd_fast; + +void __vectorcall fwd_vector(struct Foo p); +// expected-error@+1 {{parameter 'p' must have a complete type to use function 'fwd_vector' with the vectorcall calling convention}} +void (__vectorcall *fp_fwd_vector)(struct Foo) = &fwd_vector; + +template struct TemplateWrapper { + T field; // expected-error {{field has incomplete type 'Foo'}} +}; + +void __vectorcall tpl_ok(TemplateWrapper p); +void(__vectorcall *fp_tpl_ok)(TemplateWrapper) = &tpl_ok; + +void __vectorcall tpl_fast(TemplateWrapper p); +void(__vectorcall *fp_tpl_fast)(TemplateWrapper) = &tpl_fast; // expected-note {{requested here}}