Move ArgNo to static data.

http://reviews.llvm.org/D5082

Files:
  projects/compiler-rt/lib/ubsan/ubsan_handlers.cc
  projects/compiler-rt/lib/ubsan/ubsan_handlers.h
  projects/compiler-rt/test/ubsan/TestCases/Misc/nonnull-arg.cpp
  tools/clang/include/clang/Basic/Sanitizers.def
  tools/clang/lib/CodeGen/CGCall.cpp
  tools/clang/lib/CodeGen/CGClass.cpp
  tools/clang/lib/CodeGen/CGExpr.cpp
  tools/clang/lib/CodeGen/CGExprCXX.cpp
  tools/clang/lib/CodeGen/CodeGenFunction.h
  tools/clang/test/CodeGen/catch-undef-behavior.c
  tools/clang/test/Driver/fsanitize.c
Index: tools/clang/lib/CodeGen/CodeGenFunction.h
===================================================================
--- tools/clang/lib/CodeGen/CodeGenFunction.h
+++ tools/clang/lib/CodeGen/CodeGenFunction.h
@@ -2617,6 +2617,7 @@
   void EmitCallArgs(CallArgList &Args, const T *CallArgTypeInfo,
                     CallExpr::const_arg_iterator ArgBeg,
                     CallExpr::const_arg_iterator ArgEnd,
+                    const FunctionDecl *CalleeDecl = nullptr,
                     unsigned ParamsToSkip = 0, bool ForceColumnInfo = false) {
     SmallVector<QualType, 16> ArgTypes;
     CallExpr::const_arg_iterator Arg = ArgBeg;
@@ -2665,13 +2666,15 @@
     for (; Arg != ArgEnd; ++Arg)
       ArgTypes.push_back(Arg->getType());
 
-    EmitCallArgs(Args, ArgTypes, ArgBeg, ArgEnd, ForceColumnInfo);
+    EmitCallArgs(Args, ArgTypes, ArgBeg, ArgEnd, CalleeDecl, ParamsToSkip,
+                 ForceColumnInfo);
   }
 
   void EmitCallArgs(CallArgList &Args, ArrayRef<QualType> ArgTypes,
                     CallExpr::const_arg_iterator ArgBeg,
                     CallExpr::const_arg_iterator ArgEnd,
-                    bool ForceColumnInfo = false);
+                    const FunctionDecl *CalleeDecl = nullptr,
+                    unsigned ParamsToSkip = 0, bool ForceColumnInfo = false);
 
 private:
   const TargetCodeGenInfo &getTargetHooks() const {
Index: tools/clang/lib/CodeGen/CGCall.cpp
===================================================================
--- tools/clang/lib/CodeGen/CGCall.cpp
+++ tools/clang/lib/CodeGen/CGCall.cpp
@@ -1419,25 +1419,25 @@
   return CGF.Builder.CreateFPCast(value, varType, "arg.unpromote");
 }
 
-static bool shouldAddNonNullAttr(const Decl *FD, const ParmVarDecl *PVD) {
+static bool shouldAddNonNullAttr(const Decl *FD, const ParmVarDecl *PVD,
+                                 QualType ArgType, unsigned ArgNo) {
   // FIXME: __attribute__((nonnull)) can also be applied to:
   //   - references to pointers, where the pointee is known to be
   //     nonnull (apparently a Clang extension)
   //   - transparent unions containing pointers
   // In the former case, LLVM IR cannot represent the constraint. In
   // the latter case, we have no guarantee that the transparent union
   // is in fact passed as a pointer.
-  if (!PVD->getType()->isAnyPointerType() &&
-      !PVD->getType()->isBlockPointerType())
+  if (!ArgType->isAnyPointerType() && !ArgType->isBlockPointerType())
     return false;
   // First, check attribute on parameter itself.
-  if (PVD->hasAttr<NonNullAttr>())
+  if (PVD && PVD->hasAttr<NonNullAttr>())
     return true;
   // Check function attributes.
   if (!FD)
     return false;
   for (const auto *NNAttr : FD->specific_attrs<NonNullAttr>()) {
-    if (NNAttr->isNonNull(PVD->getFunctionScopeIndex()))
+    if (NNAttr->isNonNull(ArgNo))
       return true;
   }
   return false;
@@ -1577,7 +1577,8 @@
         llvm::Value *V = AI;
 
         if (const ParmVarDecl *PVD = dyn_cast<ParmVarDecl>(Arg)) {
-          if (shouldAddNonNullAttr(CurCodeDecl, PVD))
+          if (shouldAddNonNullAttr(CurCodeDecl, PVD, PVD->getType(),
+                                   PVD->getFunctionScopeIndex()))
             AI->addAttr(llvm::AttributeSet::get(getLLVMContext(),
                                                 AI->getArgNo() + 1,
                                                 llvm::Attribute::NonNull));
@@ -2404,10 +2405,35 @@
   }
 }
 
+static void emitNonNullArgCheck(CodeGenFunction &CGF, RValue RV,
+                                QualType ArgType, SourceLocation ArgLoc,
+                                const FunctionDecl *FD, unsigned ParmNum) {
+  if (!CGF.SanOpts->NonnullAttribute || !FD)
+    return;
+  auto PVD = ParmNum < FD->getNumParams() ? FD->getParamDecl(ParmNum) : nullptr;
+  unsigned ArgNo = PVD ? PVD->getFunctionScopeIndex() : ParmNum;
+  if (!shouldAddNonNullAttr(FD, PVD, ArgType, ArgNo))
+    return;
+  CodeGenFunction::SanitizerScope SanScope(&CGF);
+  assert(RV.isScalar());
+  llvm::Value *V = RV.getScalarVal();
+  llvm::Value *Cond =
+      CGF.Builder.CreateICmpNE(V, llvm::Constant::getNullValue(V->getType()));
+  llvm::Constant *StaticData[] = {
+      CGF.EmitCheckSourceLocation(ArgLoc),
+      CGF.EmitCheckSourceLocation(FD->getLocation()),
+      llvm::ConstantInt::get(CGF.Int32Ty, ArgNo + 1),
+  };
+  CGF.EmitCheck(Cond, "nonnull_arg", StaticData, None,
+                CodeGenFunction::CRK_Recoverable);
+}
+
 void CodeGenFunction::EmitCallArgs(CallArgList &Args,
                                    ArrayRef<QualType> ArgTypes,
                                    CallExpr::const_arg_iterator ArgBeg,
                                    CallExpr::const_arg_iterator ArgEnd,
+                                   const FunctionDecl *CalleeDecl,
+                                   unsigned ParamsToSkip,
                                    bool ForceColumnInfo) {
   CGDebugInfo *DI = getDebugInfo();
   SourceLocation CallLoc;
@@ -2431,6 +2457,8 @@
     for (int I = ArgTypes.size() - 1; I >= 0; --I) {
       CallExpr::const_arg_iterator Arg = ArgBeg + I;
       EmitCallArg(Args, *Arg, ArgTypes[I]);
+      emitNonNullArgCheck(*this, Args.back().RV, ArgTypes[I], Arg->getExprLoc(),
+                          CalleeDecl, ParamsToSkip + I);
       // Restore the debug location.
       if (DI) DI->EmitLocation(Builder, CallLoc, ForceColumnInfo);
     }
@@ -2445,6 +2473,8 @@
     CallExpr::const_arg_iterator Arg = ArgBeg + I;
     assert(Arg != ArgEnd);
     EmitCallArg(Args, *Arg, ArgTypes[I]);
+    emitNonNullArgCheck(*this, Args.back().RV, ArgTypes[I], Arg->getExprLoc(),
+                        CalleeDecl, ParamsToSkip + I);
     // Restore the debug location.
     if (DI) DI->EmitLocation(Builder, CallLoc, ForceColumnInfo);
   }
Index: tools/clang/lib/CodeGen/CGExprCXX.cpp
===================================================================
--- tools/clang/lib/CodeGen/CGExprCXX.cpp
+++ tools/clang/lib/CodeGen/CGExprCXX.cpp
@@ -55,20 +55,18 @@
 
   const FunctionProtoType *FPT = MD->getType()->castAs<FunctionProtoType>();
   RequiredArgs required = RequiredArgs::forPrototypePlus(FPT, Args.size());
-  
+
   // And the rest of the call args.
-  CallExpr::const_arg_iterator ArgBeg, ArgEnd;
-  if (CE == nullptr) {
-    ArgBeg = ArgEnd = nullptr;
-  } else if (auto OCE = dyn_cast<CXXOperatorCallExpr>(CE)) {
+  if (CE) {
     // Special case: skip first argument of CXXOperatorCall (it is "this").
-    ArgBeg = OCE->arg_begin() + 1;
-    ArgEnd = OCE->arg_end();
+    unsigned ArgsToSkip = isa<CXXOperatorCallExpr>(CE) ? 1 : 0;
+    EmitCallArgs(Args, FPT, CE->arg_begin() + ArgsToSkip, CE->arg_end(),
+                 CE->getDirectCallee());
   } else {
-    ArgBeg = CE->arg_begin();
-    ArgEnd = CE->arg_end();
+    assert(
+        FPT->getNumParams() == 0 &&
+        "No CallExpr specified for function with non-zero number of arguments");
   }
-  EmitCallArgs(Args, FPT, ArgBeg, ArgEnd);
 
   return EmitCall(CGM.getTypes().arrangeCXXMethodCall(Args, FPT, required),
                   Callee, ReturnValue, Args, MD);
@@ -284,7 +282,7 @@
   RequiredArgs required = RequiredArgs::forPrototypePlus(FPT, 1);
   
   // And the rest of the call args
-  EmitCallArgs(Args, FPT, E->arg_begin(), E->arg_end());
+  EmitCallArgs(Args, FPT, E->arg_begin(), E->arg_end(), E->getDirectCallee());
   return EmitCall(CGM.getTypes().arrangeCXXMethodCall(Args, FPT, required),
                   Callee, ReturnValue, Args);
 }
@@ -1238,7 +1236,8 @@
   // We start at 1 here because the first argument (the allocation size)
   // has already been emitted.
   EmitCallArgs(allocatorArgs, allocatorType, E->placement_arg_begin(),
-               E->placement_arg_end(), /*ParamsToSkip*/ 1);
+               E->placement_arg_end(), /* CalleeDecl */ nullptr,
+               /*ParamsToSkip*/ 1);
 
   // Emit the allocation call.  If the allocator is a global placement
   // operator, just "inline" it directly.
Index: tools/clang/lib/CodeGen/CGClass.cpp
===================================================================
--- tools/clang/lib/CodeGen/CGClass.cpp
+++ tools/clang/lib/CodeGen/CGClass.cpp
@@ -1674,7 +1674,7 @@
 
   // Add the rest of the user-supplied arguments.
   const FunctionProtoType *FPT = D->getType()->castAs<FunctionProtoType>();
-  EmitCallArgs(Args, FPT, E->arg_begin(), E->arg_end());
+  EmitCallArgs(Args, FPT, E->arg_begin(), E->arg_end(), E->getConstructor());
 
   // Insert any ABI-specific implicit constructor arguments.
   unsigned ExtraArgs = CGM.getCXXABI().addImplicitConstructorArgs(
@@ -1716,7 +1716,8 @@
   Args.add(RValue::get(Src), QT);
 
   // Skip over first argument (Src).
-  EmitCallArgs(Args, FPT, E->arg_begin() + 1, E->arg_end(), /*ParamsToSkip*/ 1);
+  EmitCallArgs(Args, FPT, E->arg_begin() + 1, E->arg_end(), E->getConstructor(),
+               /*ParamsToSkip*/ 1);
 
   EmitCall(CGM.getTypes().arrangeCXXMethodCall(Args, FPT, RequiredArgs::All),
            Callee, ReturnValueSlot(), Args, D);
Index: tools/clang/lib/CodeGen/CGExpr.cpp
===================================================================
--- tools/clang/lib/CodeGen/CGExpr.cpp
+++ tools/clang/lib/CodeGen/CGExpr.cpp
@@ -3320,7 +3320,8 @@
 
   CallArgList Args;
   EmitCallArgs(Args, dyn_cast<FunctionProtoType>(FnType), E->arg_begin(),
-               E->arg_end(), /*ParamsToSkip*/ 0, ForceColumnInfo);
+               E->arg_end(), E->getDirectCallee(), /*ParamsToSkip*/ 0,
+               ForceColumnInfo);
 
   const CGFunctionInfo &FnInfo =
     CGM.getTypes().arrangeFreeFunctionCall(Args, FnType);
Index: tools/clang/test/CodeGen/catch-undef-behavior.c
===================================================================
--- tools/clang/test/CodeGen/catch-undef-behavior.c
+++ tools/clang/test/CodeGen/catch-undef-behavior.c
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -fsanitize=alignment,null,object-size,shift,return,signed-integer-overflow,vla-bound,float-cast-overflow,integer-divide-by-zero,bool,returns-nonnull-attribute -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s
-// RUN: %clang_cc1 -fsanitize-undefined-trap-on-error -fsanitize=alignment,null,object-size,shift,return,signed-integer-overflow,vla-bound,float-cast-overflow,integer-divide-by-zero,bool,returns-nonnull-attribute -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK-TRAP
+// RUN: %clang_cc1 -fsanitize=alignment,null,object-size,shift,return,signed-integer-overflow,vla-bound,float-cast-overflow,integer-divide-by-zero,bool,returns-nonnull-attribute,nonnull-attribute -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s
+// RUN: %clang_cc1 -fsanitize-undefined-trap-on-error -fsanitize=alignment,null,object-size,shift,return,signed-integer-overflow,vla-bound,float-cast-overflow,integer-divide-by-zero,bool,returns-nonnull-attribute,nonnull-attribute -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK-TRAP
 // RUN: %clang_cc1 -fsanitize=null -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK-NULL
 // RUN: %clang_cc1 -fsanitize=signed-integer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK-OVERFLOW
 
@@ -446,6 +446,31 @@
   return a;
 }
 
+// CHECK-LABEL: @call_decl_nonnull
+__attribute__((nonnull)) void decl_nonnull(int *a);
+void call_decl_nonnull(int *a) {
+  // CHECK: [[OK:%.*]] = icmp ne i32* {{.*}}, null
+  // CHECK: br i1 [[OK]]
+  // CHECK: call void @__ubsan_handle_nonnull_arg
+
+  // CHECK-TRAP: [[OK:%.*]] = icmp ne i32* {{.*}}, null
+  // CHECK-TRAP: br i1 [[OK]]
+  // CHECK-TRAP: call void @llvm.trap() [[NR_NUW]]
+  // CHECK-TRAP: unreachable
+  decl_nonnull(a);
+}
+
+// CHECK-LABEL: @call_nonnull_variadic
+__attribute__((nonnull)) void nonnull_variadic(int a, ...);
+void call_nonnull_variadic(int a, int *b) {
+  // CHECK: [[OK:%.*]] = icmp ne i32* {{.*}}, null
+  // CHECK: br i1 [[OK]]
+  // CHECK: call void @__ubsan_handle_nonnull_arg
+  // CHECK-NOT: __ubsan_handle_nonnull_arg
+  // CHECK: call void (i32, ...)* @nonnull_variadic
+  nonnull_variadic(a, b);
+}
+
 // CHECK: ![[WEIGHT_MD]] = metadata !{metadata !"branch_weights", i32 1048575, i32 1}
 
 // CHECK-TRAP: attributes [[NR_NUW]] = { noreturn nounwind }
Index: tools/clang/test/Driver/fsanitize.c
===================================================================
--- tools/clang/test/Driver/fsanitize.c
+++ tools/clang/test/Driver/fsanitize.c
@@ -1,22 +1,22 @@
 // RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined-trap -fsanitize-undefined-trap-on-error %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-TRAP
 // RUN: %clang -target x86_64-linux-gnu -fsanitize-undefined-trap-on-error -fsanitize=undefined-trap %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-TRAP
-// CHECK-UNDEFINED-TRAP: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift|unreachable|return|vla-bound|alignment|null|object-size|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute),?){15}"}}
+// CHECK-UNDEFINED-TRAP: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift|unreachable|return|vla-bound|alignment|null|object-size|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute|nonnull-attribute),?){16}"}}
 // CHECK-UNDEFINED-TRAP: "-fsanitize-undefined-trap-on-error"
 
 // RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED
-// CHECK-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|function|shift|unreachable|return|vla-bound|alignment|null|vptr|object-size|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute),?){17}"}}
+// CHECK-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|function|shift|unreachable|return|vla-bound|alignment|null|vptr|object-size|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute|nonnull-attribute),?){18}"}}
 
 // RUN: %clang -target x86_64-apple-darwin10 -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-DARWIN
-// CHECK-UNDEFINED-DARWIN: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift|unreachable|return|vla-bound|alignment|null|vptr|object-size|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute),?){16}"}}
+// CHECK-UNDEFINED-DARWIN: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift|unreachable|return|vla-bound|alignment|null|vptr|object-size|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute|nonnull-attribute),?){17}"}}
 
 // RUN: %clang -target x86_64-linux-gnu -fsanitize=integer %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-INTEGER
 // CHECK-INTEGER: "-fsanitize={{((signed-integer-overflow|unsigned-integer-overflow|integer-divide-by-zero|shift),?){4}"}}
 
 // RUN: %clang -fsanitize=bounds -### -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=CHECK-BOUNDS
 // CHECK-BOUNDS: "-fsanitize={{((array-bounds|local-bounds),?){2}"}}
 
 // RUN: %clang -target x86_64-linux-gnu -fsanitize=thread,undefined -fno-sanitize=thread -fno-sanitize=float-cast-overflow,vptr,bool,enum %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-PARTIAL-UNDEFINED
-// CHECK-PARTIAL-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|function|shift|unreachable|return|vla-bound|alignment|null|object-size|array-bounds|returns-nonnull-attribute),?){13}"}}
+// CHECK-PARTIAL-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|function|shift|unreachable|return|vla-bound|alignment|null|object-size|array-bounds|returns-nonnull-attribute|nonnull-attribute),?){14}"}}
 
 // RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined -fsanitize-undefined-trap-on-error %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-TRAP-ON-ERROR-UNDEF
 // CHECK-UNDEFINED-TRAP-ON-ERROR-UNDEF: '-fsanitize=undefined' not allowed with '-fsanitize-undefined-trap-on-error'
Index: tools/clang/include/clang/Basic/Sanitizers.def
===================================================================
--- tools/clang/include/clang/Basic/Sanitizers.def
+++ tools/clang/include/clang/Basic/Sanitizers.def
@@ -59,6 +59,7 @@
 SANITIZER("float-divide-by-zero", FloatDivideByZero)
 SANITIZER("function", Function)
 SANITIZER("integer-divide-by-zero", IntegerDivideByZero)
+SANITIZER("nonnull-attribute", NonnullAttribute)
 SANITIZER("null", Null)
 SANITIZER("object-size", ObjectSize)
 SANITIZER("return", Return)
@@ -79,19 +80,20 @@
 // ABI or address space layout implications, and only catch undefined behavior.
 SANITIZER_GROUP("undefined", Undefined,
                 Alignment | Bool | ArrayBounds | Enum | FloatCastOverflow |
-                    FloatDivideByZero | Function | IntegerDivideByZero | Null |
-                    ObjectSize | Return | ReturnsNonnullAttribute | Shift |
-                    SignedIntegerOverflow | Unreachable | VLABound | Vptr)
+                    FloatDivideByZero | Function | IntegerDivideByZero |
+                    NonnullAttribute | Null | ObjectSize | Return |
+                    ReturnsNonnullAttribute | Shift | SignedIntegerOverflow |
+                    Unreachable | VLABound | Vptr)
 
 // -fsanitize=undefined-trap includes
 // all sanitizers included by -fsanitize=undefined, except those that require
 // runtime support.  This group is generally used in conjunction with the
 // -fsanitize-undefined-trap-on-error flag.
 SANITIZER_GROUP("undefined-trap", UndefinedTrap,
                 Alignment | Bool | ArrayBounds | Enum | FloatCastOverflow |
-                    FloatDivideByZero | IntegerDivideByZero | Null |
-                    ObjectSize | Return | ReturnsNonnullAttribute | Shift |
-                    SignedIntegerOverflow | Unreachable | VLABound)
+                    FloatDivideByZero | IntegerDivideByZero | NonnullAttribute |
+                    Null | ObjectSize | Return | ReturnsNonnullAttribute |
+                    Shift | SignedIntegerOverflow | Unreachable | VLABound)
 
 SANITIZER_GROUP("integer", Integer,
                 SignedIntegerOverflow | UnsignedIntegerOverflow | Shift |
Index: projects/compiler-rt/test/ubsan/TestCases/Misc/nonnull-arg.cpp
===================================================================
--- /dev/null
+++ projects/compiler-rt/test/ubsan/TestCases/Misc/nonnull-arg.cpp
@@ -0,0 +1,58 @@
+// RUN: %clangxx -fsanitize=nonnull-attribute -fno-sanitize-recover %s -O3 -o %t
+// RUN: %run %t nc
+// RUN: %run %t nm
+// RUN: %run %t nf
+// RUN: %run %t nv
+// RUN: not %run %t 0c 2>&1 | FileCheck %s --check-prefix=CTOR
+// RUN: not %run %t 0m 2>&1 | FileCheck %s --check-prefix=METHOD
+// RUN: not %run %t 0f 2>&1 | FileCheck %s --check-prefix=FUNC
+// RUN: not %run %t 0v 2>&1 | FileCheck %s --check-prefix=VARIADIC
+
+class C {
+  int *null_;
+  int *nonnull_;
+
+public:
+  C(int *null, __attribute__((nonnull)) int *nonnull)
+      : null_(null), nonnull_(nonnull) {}
+  int value() { return *nonnull_; }
+  int method(int *nonnull, int *null) __attribute__((nonnull(2))) {
+    return *nonnull_ + *nonnull;
+  }
+};
+
+__attribute__((nonnull)) int func(int *nonnull) { return *nonnull; }
+
+#include <stdarg.h>
+__attribute__((nonnull)) int variadic(int x, ...) {
+  va_list args;
+  va_start(args, x);
+  int *nonnull = va_arg(args, int*);
+  int res = *nonnull;
+  va_end(args);
+  return res;
+}
+
+int main(int argc, char *argv[]) {
+  int local = 0;
+  int *arg = (argv[1][0] == '0') ? 0x0 : &local;
+  switch (argv[1][1]) {
+    case 'c':
+      return C(0x0, arg).value();
+      // CTOR: {{.*}}nonnull-arg.cpp:[[@LINE-1]]:21: runtime error: null pointer passed as argument 2, which is declared to never be null
+      // CTOR-NEXT: {{.*}}nonnull-arg.cpp:16:3: note: function declared here
+    case 'm':
+      return C(0x0, &local).method(arg, 0x0);
+      // METHOD: {{.*}}nonnull-arg.cpp:[[@LINE-1]]:36: runtime error: null pointer passed as argument 1, which is declared to never be null
+      // METHOD-NEXT: {{.*}}nonnull-arg.cpp:19:7: note: function declared here
+    case 'f':
+      return func(arg);
+      // FUNC: {{.*}}nonnull-arg.cpp:[[@LINE-1]]:19: runtime error: null pointer passed as argument 1, which is declared to never be null
+      // FUNC-NEXT: {{.*}}nonnull-arg.cpp:24:30: note: function declared here
+    case 'v':
+      return variadic(42, arg);
+    // VARIADIC: {{.*}}nonnull-arg.cpp:[[@LINE-1]]:27: runtime error: null pointer passed as argument 2, which is declared to never be null
+    // VARIADIC-NEXT: {{.*}}nonnull-arg.cpp:27:30: note: function declared here
+  }
+  return 0;
+}
Index: projects/compiler-rt/lib/ubsan/ubsan_handlers.cc
===================================================================
--- projects/compiler-rt/lib/ubsan/ubsan_handlers.cc
+++ projects/compiler-rt/lib/ubsan/ubsan_handlers.cc
@@ -331,7 +331,7 @@
   handleFunctionTypeMismatch(Data, Function, Opts);
 }
 
-static void handleNonnullReturn(NonNullReturnData *Data, ReportOptions Opts) {
+static void handleNonNullReturn(NonNullReturnData *Data, ReportOptions Opts) {
   SourceLocation Loc = Data->Loc.acquire();
   if (Loc.isDisabled())
     return;
@@ -344,10 +344,33 @@
 
 void __ubsan::__ubsan_handle_nonnull_return(NonNullReturnData *Data) {
   GET_REPORT_OPTIONS(false);
-  handleNonnullReturn(Data, Opts);
+  handleNonNullReturn(Data, Opts);
 }
 
 void __ubsan::__ubsan_handle_nonnull_return_abort(NonNullReturnData *Data) {
   GET_REPORT_OPTIONS(true);
-  handleNonnullReturn(Data, Opts);
+  handleNonNullReturn(Data, Opts);
+}
+
+static void handleNonNullArg(NonNullArgData *Data, ReportOptions Opts) {
+  SourceLocation Loc = Data->Loc.acquire();
+  if (Loc.isDisabled())
+    return;
+
+  ScopedReport R(Opts);
+
+  Diag(Loc, DL_Error, "null pointer passed as argument %0, which is declared to "
+       "never be null") << Data->ArgIndex;
+  if (!Data->DeclLoc.isInvalid())
+    Diag(Data->DeclLoc, DL_Note, "function declared here");
+}
+
+void __ubsan::__ubsan_handle_nonnull_arg(NonNullArgData *Data) {
+  GET_REPORT_OPTIONS(false);
+  handleNonNullArg(Data, Opts);
+}
+
+void __ubsan::__ubsan_handle_nonnull_arg_abort(NonNullArgData *Data) {
+  GET_REPORT_OPTIONS(true);
+  handleNonNullArg(Data, Opts);
 }
Index: projects/compiler-rt/lib/ubsan/ubsan_handlers.h
===================================================================
--- projects/compiler-rt/lib/ubsan/ubsan_handlers.h
+++ projects/compiler-rt/lib/ubsan/ubsan_handlers.h
@@ -130,6 +130,15 @@
 /// \brief Handle returning null from function with returns_nonnull attribute.
 RECOVERABLE(nonnull_return, NonNullReturnData *Data)
 
+struct NonNullArgData {
+  SourceLocation Loc;
+  SourceLocation DeclLoc;
+  int ArgIndex;
+};
+
+/// \brief Handle passing null pointer to function with nonnull attribute.
+RECOVERABLE(nonnull_arg, NonNullArgData *Data);
+
 }
 
 #endif // UBSAN_HANDLERS_H
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to