summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/clang/Sema/Sema.h1
-rw-r--r--lib/Sema/SemaChecking.cpp67
-rw-r--r--test/SemaCXX/microsoft-varargs-diagnostics.cpp42
-rw-r--r--test/SemaCXX/microsoft-varargs.cpp22
4 files changed, 131 insertions, 1 deletions
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index 8dba089924..e254afdbad 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -8281,6 +8281,7 @@ private:
bool CheckX86BuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
bool SemaBuiltinVAStart(CallExpr *TheCall);
+ bool SemaBuiltinVAStartARM(CallExpr *Call);
bool SemaBuiltinUnorderedCompare(CallExpr *TheCall);
bool SemaBuiltinFPClassification(CallExpr *TheCall, unsigned NumArgs);
diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp
index f6bb8370d5..66be962fcf 100644
--- a/lib/Sema/SemaChecking.cpp
+++ b/lib/Sema/SemaChecking.cpp
@@ -142,10 +142,23 @@ Sema::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
break;
case Builtin::BI__builtin_stdarg_start:
case Builtin::BI__builtin_va_start:
- case Builtin::BI__va_start:
if (SemaBuiltinVAStart(TheCall))
return ExprError();
break;
+ case Builtin::BI__va_start: {
+ switch (Context.getTargetInfo().getTriple().getArch()) {
+ case llvm::Triple::arm:
+ case llvm::Triple::thumb:
+ if (SemaBuiltinVAStartARM(TheCall))
+ return ExprError();
+ break;
+ default:
+ if (SemaBuiltinVAStart(TheCall))
+ return ExprError();
+ break;
+ }
+ break;
+ }
case Builtin::BI__builtin_isgreater:
case Builtin::BI__builtin_isgreaterequal:
case Builtin::BI__builtin_isless:
@@ -1739,6 +1752,58 @@ bool Sema::SemaBuiltinVAStart(CallExpr *TheCall) {
return false;
}
+bool Sema::SemaBuiltinVAStartARM(CallExpr *Call) {
+ // void __va_start(va_list *ap, const char *named_addr, size_t slot_size,
+ // const char *named_addr);
+
+ Expr *Func = Call->getCallee();
+
+ if (Call->getNumArgs() < 3)
+ return Diag(Call->getLocEnd(),
+ diag::err_typecheck_call_too_few_args_at_least)
+ << 0 /*function call*/ << 3 << Call->getNumArgs();
+
+ // Determine whether the current function is variadic or not.
+ bool IsVariadic;
+ if (BlockScopeInfo *CurBlock = getCurBlock())
+ IsVariadic = CurBlock->TheDecl->isVariadic();
+ else if (FunctionDecl *FD = getCurFunctionDecl())
+ IsVariadic = FD->isVariadic();
+ else if (ObjCMethodDecl *MD = getCurMethodDecl())
+ IsVariadic = MD->isVariadic();
+ else
+ llvm_unreachable("unexpected statement type");
+
+ if (!IsVariadic) {
+ Diag(Func->getLocStart(), diag::err_va_start_used_in_non_variadic_function);
+ return true;
+ }
+
+ // Type-check the first argument normally.
+ if (checkBuiltinArgument(*this, Call, 0))
+ return true;
+
+ static const struct {
+ unsigned ArgNo;
+ QualType Type;
+ } ArgumentTypes[] = {
+ { 1, Context.getPointerType(Context.CharTy.withConst()) },
+ { 2, Context.getSizeType() },
+ };
+
+ for (const auto &AT : ArgumentTypes) {
+ const Expr *Arg = Call->getArg(AT.ArgNo)->IgnoreParens();
+ if (Arg->getType().getCanonicalType() == AT.Type.getCanonicalType())
+ continue;
+ Diag(Arg->getLocStart(), diag::err_typecheck_convert_incompatible)
+ << Arg->getType() << AT.Type << 1 /* different class */
+ << 0 /* qualifier difference */ << 3 /* parameter mismatch */
+ << AT.ArgNo + 1 << Arg->getType() << AT.Type;
+ }
+
+ return false;
+}
+
/// SemaBuiltinUnorderedCompare - Handle functions like __builtin_isgreater and
/// friends. This is declared to take (...), so we have to check everything.
bool Sema::SemaBuiltinUnorderedCompare(CallExpr *TheCall) {
diff --git a/test/SemaCXX/microsoft-varargs-diagnostics.cpp b/test/SemaCXX/microsoft-varargs-diagnostics.cpp
new file mode 100644
index 0000000000..0b76fdd92d
--- /dev/null
+++ b/test/SemaCXX/microsoft-varargs-diagnostics.cpp
@@ -0,0 +1,42 @@
+// RUN: %clang_cc1 -triple thumbv7-windows -fms-compatibility -fsyntax-only %s -verify
+
+extern "C" {
+typedef char * va_list;
+}
+
+void test_no_arguments(int i, ...) {
+ __va_start(); // expected-error{{too few arguments to function call, expected at least 3, have 0}}
+}
+
+void test_one_argument(int i, ...) {
+ va_list ap;
+ __va_start(&ap); // expected-error{{too few arguments to function call, expected at least 3, have 1}}
+}
+
+void test_two_arguments(int i, ...) {
+ va_list ap;
+ __va_start(&ap, &i); // expected-error{{too few arguments to function call, expected at least 3, have 2}}
+}
+
+void test_non_last_argument(int i, int j, ...) {
+ va_list ap;
+ __va_start(&ap, &i, 4);
+ // expected-error@-1{{passing 'int *' to parameter of incompatible type 'const char *': type mismatch at 2nd parameter ('int *' vs 'const char *')}}
+ // expected-error@-2{{passing 'int' to parameter of incompatible type 'unsigned int': type mismatch at 3rd parameter ('int' vs 'unsigned int')}}
+}
+
+void test_stack_allocated(int i, ...) {
+ va_list ap;
+ int j;
+ __va_start(&ap, &j, 4);
+ // expected-error@-1{{passing 'int *' to parameter of incompatible type 'const char *': type mismatch at 2nd parameter ('int *' vs 'const char *')}}
+ // expected-error@-2{{passing 'int' to parameter of incompatible type 'unsigned int': type mismatch at 3rd parameter ('int' vs 'unsigned int')}}
+}
+
+void test_non_pointer_addressof(int i, ...) {
+ va_list ap;
+ __va_start(&ap, 1, 4);
+ // expected-error@-1{{passing 'int' to parameter of incompatible type 'const char *': type mismatch at 2nd parameter ('int' vs 'const char *')}}
+ // expected-error@-2{{passing 'int' to parameter of incompatible type 'unsigned int': type mismatch at 3rd parameter ('int' vs 'unsigned int')}}
+}
+
diff --git a/test/SemaCXX/microsoft-varargs.cpp b/test/SemaCXX/microsoft-varargs.cpp
new file mode 100644
index 0000000000..35f31a97c4
--- /dev/null
+++ b/test/SemaCXX/microsoft-varargs.cpp
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 -triple thumbv7-windows -fms-compatibility -fsyntax-only %s -verify
+// expected-no-diagnostics
+
+extern "C" {
+typedef char * va_list;
+void __va_start(va_list *, ...);
+}
+
+int test___va_start(int i, ...) {
+ va_list ap;
+ __va_start(&ap, ( &reinterpret_cast<const char &>(i) ),
+ ( (sizeof(i) + 4 - 1) & ~(4 - 1) ),
+ ( &reinterpret_cast<const char &>(i) ));
+ return (*(int *)((ap += ( (sizeof(int) + 4 - 1) & ~(4 - 1) ) + ( ((va_list)0 - (ap)) & (__alignof(int) - 1) )) - ( (sizeof(int) + 4 - 1) & ~(4 - 1) )));
+}
+
+int builtin(int i, ...) {
+ __builtin_va_list ap;
+ __builtin_va_start(ap, i);
+ return __builtin_va_arg(ap, int);
+}
+