george.burgess.iv updated this revision to Diff 37935.
george.burgess.iv added a comment.

Rebased


http://reviews.llvm.org/D13263

Files:
  include/clang/AST/Expr.h
  include/clang/AST/Type.h
  include/clang/Basic/Attr.td
  include/clang/Basic/AttrDocs.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/Initialization.h
  include/clang/Sema/Overload.h
  include/clang/Sema/Sema.h
  include/clang/Sema/TemplateDeduction.h
  lib/AST/ASTContext.cpp
  lib/AST/ExprConstant.cpp
  lib/AST/ItaniumMangle.cpp
  lib/AST/MicrosoftMangle.cpp
  lib/AST/Type.cpp
  lib/AST/TypePrinter.cpp
  lib/CodeGen/CGBuiltin.cpp
  lib/CodeGen/CGCall.cpp
  lib/CodeGen/CodeGenFunction.cpp
  lib/CodeGen/CodeGenFunction.h
  lib/Sema/SemaDeclAttr.cpp
  lib/Sema/SemaExpr.cpp
  lib/Sema/SemaInit.cpp
  lib/Sema/SemaOverload.cpp
  lib/Sema/SemaTemplate.cpp
  lib/Sema/SemaType.cpp
  test/CodeGen/pass-object-size.c
  test/Sema/pass-object-size.c
  test/SemaCXX/pass-object-size.cpp

Index: test/SemaCXX/pass-object-size.cpp
===================================================================
--- /dev/null
+++ test/SemaCXX/pass-object-size.cpp
@@ -0,0 +1,89 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11
+
+namespace simple {
+int Foo(void *const p __attribute__((pass_object_size(0))));
+
+int OvlFoo(void *const p __attribute__((pass_object_size(0))));
+int OvlFoo(void *const p, int);
+
+struct Statics {
+  static int Foo(void *const p __attribute__((pass_object_size(0))));
+  static int OvlFoo(void *const p __attribute__((pass_object_size(0))));
+  static int OvlFoo(void *const p __attribute__((pass_object_size(1))));
+};
+
+struct Members {
+  int Foo(void *const p __attribute__((pass_object_size(0))));
+  int OvlFoo(void *const p __attribute__((pass_object_size(0))));
+  int OvlFoo(void *const p, int);
+};
+
+void Decls() {
+  int (*A)(void *) = &Foo; //expected-error{{functions with pass_object_size params cannot have their address taken}}
+  int (*B)(void *) = Foo; //expected-error{{functions with pass_object_size params cannot have their address taken}}
+
+  int (*C)(void *) = &OvlFoo; //expected-error{{address of overloaded function 'OvlFoo' does not match required type 'int (void *)'}} expected-note@6{{candidate function made ineligible by pass_object_size attributes}} expected-note@7{{candidate function has different number of parameters (expected 1 but has 2)}}
+  int (*D)(void *) = OvlFoo; //expected-error{{address of overloaded function 'OvlFoo' does not match required type 'int (void *)'}} expected-note@6{{candidate function made ineligible by pass_object_size attributes}} expected-note@7{{candidate function has different number of parameters (expected 1 but has 2)}}
+
+  int (*E)(void *) = &Statics::Foo; //expected-error{{functions with pass_object_size params cannot have their address taken}}
+  int (*F)(void *) = &Statics::OvlFoo; //expected-error{{address of overloaded function 'OvlFoo' does not match required type 'int (void *)'}} expected-note@11{{candidate function made ineligible by pass_object_size attributes}} expected-note@12{{candidate function made ineligible by pass_object_size attributes}}
+
+  int (*G)(void *) = &Members::Foo; //expected-error{{functions with pass_object_size params cannot have their address taken}}
+  int (*H)(void *) = &Members::OvlFoo; //expected-error{{address of overloaded function 'OvlFoo' does not match required type 'int (void *)'}} expected-note@17{{candidate function made ineligible by pass_object_size attributes}} expected-note@18{{candidate function has different number of parameters (expected 1 but has 2)}}
+}
+
+void Assigns() {
+  int (*A)(void *);
+  A = &Foo; //expected-error{{functions with pass_object_size params cannot have their address taken}}
+  A = Foo; //expected-error{{assigning to 'int (*)(void *)' from incompatible type 'int (void *const pass_object_size)': mismatch between parameters with pass_object_size attributes}}
+
+  A = &OvlFoo; //expected-error{{assigning to 'int (*)(void *)' from incompatible type '<overloaded function type>'}} expected-note@6{{candidate function made ineligible by pass_object_size attributes}} expected-note@7{{candidate function has different number of parameters (expected 1 but has 2)}}
+  A = OvlFoo; //expected-error{{assigning to 'int (*)(void *)' from incompatible type '<overloaded function type>'}} expected-note@6{{candidate function made ineligible by pass_object_size attributes}} expected-note@7{{candidate function has different number of parameters (expected 1 but has 2)}}
+
+  A = &Statics::Foo; //expected-error{{functions with pass_object_size params cannot have their address taken}}
+  A = &Statics::OvlFoo; //expected-error{{assigning to 'int (*)(void *)' from incompatible type '<overloaded function type>'}} expected-note@11{{candidate function made ineligible by pass_object_size attributes}} expected-note@12{{candidate function made ineligible by pass_object_size attributes}}
+
+  int (Members::*M)(void *);
+  M = &Members::Foo; //expected-error{{functions with pass_object_size params cannot have their address taken}}
+  M = &Members::OvlFoo; //expected-error{{assigning to 'int (simple::Members::*)(void *)' from incompatible type '<overloaded function type>'}} expected-note@17{{candidate function made ineligible by pass_object_size attributes}} expected-note@18{{candidate function has different number of parameters (expected 1 but has 2)}}
+}
+
+} // namespace simple
+
+namespace templates {
+template <typename T>
+int Foo(void *const __attribute__((pass_object_size(0))))
+  { return 0; }
+
+template <typename T>
+struct Bar {
+  template <typename U>
+  int Foo(void *const __attribute__((pass_object_size(0))))
+    { return 0; }
+};
+
+void Decls() {
+  int (*A)(void *) = &Foo<void*>; //expected-error{{address of overloaded function 'Foo' does not match required type 'int (void *)'}} expected-note@55{{candidate template is ineligible because pass_object_size attribute is present}}
+  int (Bar<int>::*B)(void *) = &Bar<int>::Foo<double>; //expected-error{{address of overloaded function 'Foo' does not match required type 'int (void *)'}} expected-note@61{{candidate template is ineligible because pass_object_size attribute is present}}
+}
+
+void Assigns() {
+  int (*A)(void *);
+  A = &Foo<void*>; // expected-error{{assigning to 'int (*)(void *)' from incompatible type '<overloaded function type>'}} expected-note@55{{candidate function made ineligible by pass_object_size attributes}}
+  int (Bar<int>::*B)(void *) = &Bar<int>::Foo<double>; //expected-error{{address of overloaded function 'Foo' does not match required type 'int (void *)'}} expected-note@61{{candidate template is ineligible because pass_object_size attribute is present}}
+}
+} // namespace templates
+
+namespace virt {
+struct Foo {
+  virtual void DoIt(void *const p __attribute__((pass_object_size(0))));
+};
+
+struct Bar : public Foo {
+  void DoIt(void *const p __attribute__((pass_object_size(0)))) override; // OK
+};
+
+struct Baz : public Foo {
+  void DoIt(void *const p) override; //expected-error{{non-virtual member function marked 'override' hides virtual member function}} expected-note@79{{hidden overloaded virtual function 'virt::Foo::DoIt' declared here}}
+};
+}
Index: test/Sema/pass-object-size.c
===================================================================
--- /dev/null
+++ test/Sema/pass-object-size.c
@@ -0,0 +1,79 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s -triple x86_64-linux-gnu
+//
+// Tests for the pass_object_size attribute
+// Non-failure cases are covered in test/CodeGen/pass-object-size.c
+
+void a(void *const p __attribute__((pass_object_size))); //expected-error{{'pass_object_size' attribute takes one argument}}
+
+void b(void *const p __attribute__((pass_object_size("foo")))); //expected-error{{'pass_object_size' attribute requires an integer constant}}
+
+void c(void *const p __attribute__((pass_object_size(4)))); //expected-error{{'pass_object_size' attribute parameter 1 is out of bounds}}
+void d(void *const p __attribute__((pass_object_size(-1)))); //expected-error{{'pass_object_size' attribute requires an integer constant}}
+
+void e(char p __attribute__((pass_object_size(0)))); //expected-error{{'pass_object_size' attribute only applies to constant pointer arguments}}
+void f(const char p __attribute__((pass_object_size(0)))); //expected-error{{'pass_object_size' attribute only applies to constant pointer arguments}}
+void g(const char *p __attribute__((pass_object_size(0)))); //expected-error{{'pass_object_size' attribute only applies to constant pointer arguments}}
+void h(char *const p __attribute__((pass_object_size(0), pass_object_size(1)))); //expected-error{{'pass_object_size' attribute can only be applied once per parameter}}
+
+#define PS(N) __attribute__((pass_object_size(N)))
+#define overloaded __attribute__((overloadable))
+void Overloaded(void *const p PS(0)) overloaded;
+void Overloaded(void *const p PS(1)) overloaded;
+void Overloaded2(void *const p PS(1), void *const p2 PS(0)) overloaded;
+void Overloaded2(void *const p PS(0), void *const p2 PS(1)) overloaded;
+
+void Foo() {
+  int t, *p;
+  Overloaded(&t); //expected-error{{call to 'Overloaded' is ambiguous}} expected-note@20{{candidate function}} expected-note@21{{candidate function}}
+  Overloaded2(&t, &t); //expected-error{{call to 'Overloaded2' is ambiguous}} expected-note@22{{candidate function}} expected-note@23{{candidate function}}
+  Overloaded(p); //expected-error{{no matching function for call to 'Overloaded'}} expected-note@20{{cannot statically determine the size of parameter 1}} expected-note@21{{cannot statically determine the size of parameter 1}}
+  Overloaded2(p, p); //expected-error{{no matching function for call to 'Overloaded2'}} expected-note@22{{cannot statically determine the size of parameter 1}} expected-note@23{{cannot statically determine the size of parameter 1}}
+}
+
+void Overloaded3(void *const p PS(0), void *const p2) overloaded;
+void Overloaded3(void *const p, void *const p2 PS(0)) overloaded;
+
+void Bar() {
+  int t, *p;
+  Overloaded3(&t, &t); //expected-error{{call to 'Overloaded3' is ambiguous}} expected-note@33{{candidate function}} expected-note@34{{candidate function}}
+  Overloaded3(p, p); //expected-error{{no matching function for call to 'Overloaded3'}} expected-note@33{{cannot statically determine the size of parameter 1}} expected-note@34{{cannot statically determine the size of parameter 2}}
+}
+
+void NotOverloaded(void *const p PS(0));
+void Baz() {
+  int *p;
+  NotOverloaded(p); //expected-error{{no matching function for call to 'NotOverloaded'}} expected-note@42{{cannot statically determine the size of parameter 1}}
+}
+
+void FunctionPtrs() {
+  void (*p)(void *) = NotOverloaded; //expected-error{{functions with pass_object_size params cannot have their address taken}}
+  void (*p2)(void *) = &NotOverloaded; //expected-error{{functions with pass_object_size params cannot have their address taken}}
+
+  // FIXME: Enable these when __attribute__((overloadable)) plays better with
+  // function pointers
+#if 0
+  void (*p3)(void *) = Overloaded;
+  void (*p4)(void *) = &Overloaded;
+#endif
+}
+
+void DifferingObjectSize0(void *const p __attribute__((pass_object_size(0))));
+void DifferingObjectSize1(void *const p __attribute__((pass_object_size(1))));
+void DifferingObjectSize2(void *const p __attribute__((pass_object_size(2))));
+void DifferingObjectSize3(void *const p __attribute__((pass_object_size(3))));
+
+void DifferingObjectSize0(void *const p __attribute__((pass_object_size(0)))) {
+  DifferingObjectSize1(p); // OK
+}
+
+void DifferingObjectSize1(void *const p __attribute__((pass_object_size(1)))) {
+  DifferingObjectSize0(p); //expected-error{{no matching function for call to 'DifferingObjectSize0'}} expected-note@65{{cannot statically determine the size of parameter 1}}
+}
+
+void DifferingObjectSize2(void *const p __attribute__((pass_object_size(2)))) {
+  DifferingObjectSize3(p); //expected-error{{no matching function for call to 'DifferingObjectSize3'}} expected-note@63{{cannot statically determine the size of parameter 1}}
+}
+
+void DifferingObjectSize3(void *const p __attribute__((pass_object_size(3)))) {
+  DifferingObjectSize2(p); // OK
+}
Index: test/CodeGen/pass-object-size.c
===================================================================
--- /dev/null
+++ test/CodeGen/pass-object-size.c
@@ -0,0 +1,284 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -O0 %s -o - 2>&1 | FileCheck %s
+
+typedef unsigned long size_t;
+
+struct Foo {
+  int t[10];
+};
+
+#define PS(N) __attribute__((pass_object_size(N)))
+
+int gi = 0;
+
+// CHECK-LABEL: define i32 @ObjectSize0(i8* %{{.*}}, i64)
+int ObjectSize0(void *const p PS(0)) {
+  // CHECK-NOT: @llvm.objectsize
+  return __builtin_object_size(p, 0);
+}
+
+// CHECK-LABEL: define i32 @ObjectSize1(i8* %{{.*}}, i64)
+int ObjectSize1(void *const p PS(1)) {
+  // CHECK-NOT: @llvm.objectsize
+  return __builtin_object_size(p, 1);
+}
+
+// CHECK-LABEL: define i32 @ObjectSize2(i8* %{{.*}}, i64)
+int ObjectSize2(void *const p PS(2)) {
+  // CHECK-NOT: @llvm.objectsize
+  return __builtin_object_size(p, 2);
+}
+
+// CHECK-LABEL: define i32 @ObjectSize3(i8* %{{.*}}, i64)
+int ObjectSize3(void *const p PS(3)) {
+  // CHECK-NOT: @llvm.objectsize
+  return __builtin_object_size(p, 3);
+}
+
+// CHECK-LABEL: define void @test1
+void test1() {
+  struct Foo t[10];
+
+  // CHECK: call i32 @ObjectSize0(i8* %{{.*}}, i64 360)
+  gi = ObjectSize0(&t[1]);
+  // CHECK: call i32 @ObjectSize1(i8* %{{.*}}, i64 360)
+  gi = ObjectSize1(&t[1]);
+  // CHECK: call i32 @ObjectSize2(i8* %{{.*}}, i64 360)
+  gi = ObjectSize2(&t[1]);
+  // CHECK: call i32 @ObjectSize3(i8* %{{.*}}, i64 360)
+  gi = ObjectSize3(&t[1]);
+
+  // CHECK: call i32 @ObjectSize0(i8* %{{.*}}, i64 356)
+  gi = ObjectSize0(&t[1].t[1]);
+  // CHECK: call i32 @ObjectSize1(i8* %{{.*}}, i64 36)
+  gi = ObjectSize1(&t[1].t[1]);
+  // CHECK: call i32 @ObjectSize2(i8* %{{.*}}, i64 356)
+  gi = ObjectSize2(&t[1].t[1]);
+  // CHECK: call i32 @ObjectSize3(i8* %{{.*}}, i64 36)
+  gi = ObjectSize3(&t[1].t[1]);
+}
+
+// CHECK-LABEL: define void @test2
+void test2(struct Foo *t) {
+  // CHECK: call i32 @ObjectSize1(i8* %{{.*}}, i64 36)
+  gi = ObjectSize1(&t->t[1]);
+  // CHECK: call i32 @ObjectSize3(i8* %{{.*}}, i64 36)
+  gi = ObjectSize3(&t->t[1]);
+}
+
+// CHECK-LABEL: define i32 @_Z27NoViableOverloadObjectSize0
+int NoViableOverloadObjectSize0(void *const p) __attribute__((overloadable)) {
+  // CHECK: @llvm.objectsize
+  return __builtin_object_size(p, 0);
+}
+
+// CHECK-LABEL: define i32 @_Z27NoViableOverloadObjectSize1
+int NoViableOverloadObjectSize1(void *const p) __attribute__((overloadable)) {
+  // CHECK: @llvm.objectsize
+  return __builtin_object_size(p, 1);
+}
+
+// CHECK-LABEL: define i32 @_Z27NoViableOverloadObjectSize2
+int NoViableOverloadObjectSize2(void *const p) __attribute__((overloadable)) {
+  // CHECK: @llvm.objectsize
+  return __builtin_object_size(p, 2);
+}
+
+// CHECK-LABEL: define i32 @_Z27NoViableOverloadObjectSize3
+int NoViableOverloadObjectSize3(void *const p) __attribute__((overloadable)) {
+  // CHECK-NOT: @llvm.objectsize
+  return __builtin_object_size(p, 3);
+}
+
+// CHECK-LABEL: define i32 @_Z27NoViableOverloadObjectSize0_PS0T0_PE
+// CHECK-NOT: @llvm.objectsize
+int NoViableOverloadObjectSize0(void *const p PS(0))
+    __attribute__((overloadable)) {
+  return __builtin_object_size(p, 0);
+}
+
+int NoViableOverloadObjectSize1(void *const p PS(1))
+    __attribute__((overloadable)) {
+  return __builtin_object_size(p, 1);
+}
+
+int NoViableOverloadObjectSize2(void *const p PS(2))
+    __attribute__((overloadable)) {
+  return __builtin_object_size(p, 2);
+}
+
+int NoViableOverloadObjectSize3(void *const p PS(3))
+    __attribute__((overloadable)) {
+  return __builtin_object_size(p, 3);
+}
+
+const static int SHOULDNT_BE_CALLED = -100;
+int NoViableOverloadObjectSize0(void *const p PS(0))
+    __attribute__((overloadable, enable_if(p == 0, "never selected"))) {
+  return SHOULDNT_BE_CALLED;
+}
+
+int NoViableOverloadObjectSize1(void *const p PS(1))
+    __attribute__((overloadable, enable_if(p == 0, "never selected"))) {
+  return SHOULDNT_BE_CALLED;
+}
+
+int NoViableOverloadObjectSize2(void *const p PS(2))
+    __attribute__((overloadable, enable_if(p == 0, "never selected"))) {
+  return SHOULDNT_BE_CALLED;
+}
+
+int NoViableOverloadObjectSize3(void *const p PS(3))
+    __attribute__((overloadable, enable_if(p == 0, "never selected"))) {
+  return SHOULDNT_BE_CALLED;
+}
+
+// CHECK-LABEL: define void @test3
+void test3() {
+  struct Foo t[10];
+
+  // CHECK: call i32 @_Z27NoViableOverloadObjectSize0_PS0T0_PEPv(i8* %{{.*}}, i64 360)
+  gi = NoViableOverloadObjectSize0(&t[1]);
+  // CHECK: call i32 @_Z27NoViableOverloadObjectSize1_PS0T1_PEPv(i8* %{{.*}}, i64 360)
+  gi = NoViableOverloadObjectSize1(&t[1]);
+  // CHECK: call i32 @_Z27NoViableOverloadObjectSize2_PS0T2_PEPv(i8* %{{.*}}, i64 360)
+  gi = NoViableOverloadObjectSize2(&t[1]);
+  // CHECK: call i32 @_Z27NoViableOverloadObjectSize3_PS0T3_PEPv(i8* %{{.*}}, i64 360)
+  gi = NoViableOverloadObjectSize3(&t[1]);
+
+  // CHECK: call i32 @_Z27NoViableOverloadObjectSize0_PS0T0_PEPv(i8* %{{.*}}, i64 356)
+  gi = NoViableOverloadObjectSize0(&t[1].t[1]);
+  // CHECK: call i32 @_Z27NoViableOverloadObjectSize1_PS0T1_PEPv(i8* %{{.*}}, i64 36)
+  gi = NoViableOverloadObjectSize1(&t[1].t[1]);
+  // CHECK: call i32 @_Z27NoViableOverloadObjectSize2_PS0T2_PEPv(i8* %{{.*}}, i64 356)
+  gi = NoViableOverloadObjectSize2(&t[1].t[1]);
+  // CHECK: call i32 @_Z27NoViableOverloadObjectSize3_PS0T3_PEPv(i8* %{{.*}}, i64 36)
+  gi = NoViableOverloadObjectSize3(&t[1].t[1]);
+}
+
+// CHECK-LABEL: define void @test4
+void test4(struct Foo *t) {
+  // CHECK: call i32 @_Z27NoViableOverloadObjectSize0Pv(i8* %{{.*}})
+  gi = NoViableOverloadObjectSize0(&t[1]);
+  // CHECK: call i32 @_Z27NoViableOverloadObjectSize1Pv(i8* %{{.*}})
+  gi = NoViableOverloadObjectSize1(&t[1]);
+  // CHECK: call i32 @_Z27NoViableOverloadObjectSize2Pv(i8* %{{.*}})
+  gi = NoViableOverloadObjectSize2(&t[1]);
+  // CHECK: call i32 @_Z27NoViableOverloadObjectSize3Pv(i8* %{{.*}})
+  gi = NoViableOverloadObjectSize3(&t[1]);
+
+  // CHECK: call i32 @_Z27NoViableOverloadObjectSize0Pv(i8* %{{.*}})
+  gi = NoViableOverloadObjectSize0(&t[1].t[1]);
+  // CHECK: call i32 @_Z27NoViableOverloadObjectSize1_PS0T1_PEPv(i8* %{{.*}}, i64 36)
+  gi = NoViableOverloadObjectSize1(&t[1].t[1]);
+  // CHECK: call i32 @_Z27NoViableOverloadObjectSize2Pv(i8* %{{.*}})
+  gi = NoViableOverloadObjectSize2(&t[1].t[1]);
+  // CHECK: call i32 @_Z27NoViableOverloadObjectSize3_PS0T3_PEPv(i8* %{{.*}}, i64 36)
+  gi = NoViableOverloadObjectSize3(&t[1].t[1]);
+}
+
+// CHECK-LABEL: define i32 @IndirectObjectSize0
+int IndirectObjectSize0(void *const p PS(0)) {
+  // CHECK: call i32 @ObjectSize0(i8* %{{.*}}, i64 %{{.*}})
+  // CHECK-NOT: @llvm.objectsize
+  return ObjectSize0(p);
+}
+
+// CHECK-LABEL: define i32 @IndirectObjectSize1
+int IndirectObjectSize1(void *const p PS(1)) {
+  // CHECK: call i32 @ObjectSize1(i8* %{{.*}}, i64 %{{.*}})
+  // CHECK-NOT: @llvm.objectsize
+  return ObjectSize1(p);
+}
+
+// CHECK-LABEL: define i32 @IndirectObjectSize2
+int IndirectObjectSize2(void *const p PS(2)) {
+  // CHECK: call i32 @ObjectSize2(i8* %{{.*}}, i64 %{{.*}})
+  // CHECK-NOT: @llvm.objectsize
+  return ObjectSize2(p);
+}
+
+// CHECK-LABEL: define i32 @IndirectObjectSize3
+int IndirectObjectSize3(void *const p PS(3)) {
+  // CHECK: call i32 @ObjectSize3(i8* %{{.*}}, i64 %{{.*}})
+  // CHECK-NOT: @llvm.objectsize
+  return ObjectSize3(p);
+}
+
+int Overload0(void *, size_t, void *, size_t);
+int Overload1(void *, size_t, void *);
+int Overload2(void *, void *, size_t);
+int OverloadNoSize(void *, void *);
+
+int OverloadedObjectSize(void *const p PS(0),
+                         void *const c PS(0))
+    __attribute__((overloadable)) __asm__("Overload0");
+
+int OverloadedObjectSize(void *const p PS(0),
+                         void *const c)
+    __attribute__((overloadable)) __asm__("Overload1");
+
+int OverloadedObjectSize(void *const p,
+                         void *const c PS(0))
+    __attribute__((overloadable)) __asm__("Overload2");
+
+int OverloadedObjectSize(void *const p, void *const c)
+    __attribute__((overloadable)) __asm__("OverloadNoSize");
+
+// CHECK-LABEL: define void @test7
+void test7() {
+  int known[10], *opaque;
+
+  // CHECK: call i32 @"\01Overload0"
+  gi = OverloadedObjectSize(&known[0], &known[0]);
+
+  // CHECK: call i32 @"\01Overload1"
+  gi = OverloadedObjectSize(&known[0], opaque);
+
+  // CHECK: call i32 @"\01Overload2"
+  gi = OverloadedObjectSize(opaque, &known[0]);
+
+  // CHECK: call i32 @"\01OverloadNoSize"
+  gi = OverloadedObjectSize(opaque, opaque);
+}
+
+int Identity(void *p, size_t i) { return i; }
+
+// CHECK-NOT: define void @AsmObjectSize
+int AsmObjectSize0(void *const p PS(0)) __asm__("Identity");
+
+int AsmObjectSize1(void *const p PS(1)) __asm__("Identity");
+
+int AsmObjectSize2(void *const p PS(2)) __asm__("Identity");
+
+int AsmObjectSize3(void *const p PS(3)) __asm__("Identity");
+
+// CHECK-LABEL: define void @test10
+void test10() {
+  struct Foo t[10];
+
+  // CHECK: call i32 @"\01Identity"(i8* %{{.*}}, i64 360)
+  gi = AsmObjectSize0(&t[1]);
+  // CHECK: call i32 @"\01Identity"(i8* %{{.*}}, i64 360)
+  gi = AsmObjectSize1(&t[1]);
+  // CHECK: call i32 @"\01Identity"(i8* %{{.*}}, i64 360)
+  gi = AsmObjectSize2(&t[1]);
+  // CHECK: call i32 @"\01Identity"(i8* %{{.*}}, i64 360)
+  gi = AsmObjectSize3(&t[1]);
+
+  // CHECK: call i32 @"\01Identity"(i8* %{{.*}}, i64 356)
+  gi = AsmObjectSize0(&t[1].t[1]);
+  // CHECK: call i32 @"\01Identity"(i8* %{{.*}}, i64 36)
+  gi = AsmObjectSize1(&t[1].t[1]);
+  // CHECK: call i32 @"\01Identity"(i8* %{{.*}}, i64 356)
+  gi = AsmObjectSize2(&t[1].t[1]);
+  // CHECK: call i32 @"\01Identity"(i8* %{{.*}}, i64 36)
+  gi = AsmObjectSize3(&t[1].t[1]);
+}
+
+// CHECK-LABEL: define void @test11
+void test11(struct Foo *t) {
+  // CHECK: call i32 @"\01Identity"(i8* %{{.*}}, i64 36)
+  gi = AsmObjectSize1(&t[1].t[1]);
+  // CHECK: call i32 @"\01Identity"(i8* %{{.*}}, i64 36)
+  gi = AsmObjectSize3(&t[1].t[1]);
+}
Index: lib/Sema/SemaType.cpp
===================================================================
--- lib/Sema/SemaType.cpp
+++ lib/Sema/SemaType.cpp
@@ -3927,6 +3927,10 @@
         ConsumedParameters.reserve(FTI.NumParams);
         bool HasAnyConsumedParameters = false;
 
+        // Lazily resized/initialized when we find our first pass_object_size
+        // param
+        SmallVector<bool, 16> PassObjectSizeParameters;
+
         for (unsigned i = 0, e = FTI.NumParams; i != e; ++i) {
           ParmVarDecl *Param = cast<ParmVarDecl>(FTI.Params[i].Param);
           QualType ParamTy = Param->getType();
@@ -3989,12 +3993,21 @@
             HasAnyConsumedParameters |= Consumed;
           }
 
+          if (Param->hasAttr<PassObjectSizeAttr>()) {
+            if (PassObjectSizeParameters.empty())
+              PassObjectSizeParameters.resize(FTI.NumParams);
+            PassObjectSizeParameters[i] = true;
+          }
+
           ParamTys.push_back(ParamTy);
         }
 
         if (HasAnyConsumedParameters)
           EPI.ConsumedParameters = ConsumedParameters.data();
 
+        if (!PassObjectSizeParameters.empty())
+          EPI.PassObjectSizeParams = PassObjectSizeParameters.data();
+
         SmallVector<QualType, 4> Exceptions;
         SmallVector<ParsedType, 2> DynamicExceptions;
         SmallVector<SourceRange, 2> DynamicExceptionRanges;
Index: lib/Sema/SemaTemplate.cpp
===================================================================
--- lib/Sema/SemaTemplate.cpp
+++ lib/Sema/SemaTemplate.cpp
@@ -2691,7 +2691,8 @@
   typedef PartialSpecMatchResult MatchResult;
   SmallVector<MatchResult, 4> Matched;
   SourceLocation PointOfInstantiation = TemplateNameLoc;
-  TemplateSpecCandidateSet FailedCandidates(PointOfInstantiation);
+  TemplateSpecCandidateSet FailedCandidates(PointOfInstantiation,
+                                            /*ForTakingAddress=*/false);
 
   // 1. Attempt to find the closest partial specialization that this
   // specializes, if any.
@@ -6754,7 +6755,8 @@
   // The set of function template specializations that could match this
   // explicit function template specialization.
   UnresolvedSet<8> Candidates;
-  TemplateSpecCandidateSet FailedCandidates(FD->getLocation());
+  TemplateSpecCandidateSet FailedCandidates(FD->getLocation(),
+                                            /*ForTakingAddress=*/false);
 
   llvm::SmallDenseMap<FunctionDecl *, TemplateArgumentListInfo, 8>
       ConvertedTemplateArgs;
Index: lib/Sema/SemaOverload.cpp
===================================================================
--- lib/Sema/SemaOverload.cpp
+++ lib/Sema/SemaOverload.cpp
@@ -38,6 +38,68 @@
 using namespace clang;
 using namespace sema;
 
+/// Checks if using the result of __builtin_object_size(p, @p From) in place of
+/// __builtin_object_size(p, @p To) is correct
+/// TODO: Duplicated in lib/CodeGen/CGBuiltin.cpp. Maybe refactor?
+static bool areBOSTypesCompatible(int From, int To) {
+  // Note: Our __builtin_object_size implementation currently treats Type=0 and
+  // Type=2 identically. Encoding this implementation detail here may make
+  // improving __builtin_object_size difficult in the future, so it's omitted.
+  return From == To || (From == 0 && To == 1) || (From == 3 && To == 2);
+}
+
+/// Determines whether it's possible to evaluate __builtin_object_size on the
+/// given Expr with the given Type. This is aware of the pass_object_size
+/// attribute.
+static bool canEvaluateObjectSizeOn(ASTContext &Context, const Expr *E,
+                                    int Type) {
+  auto *DRE = dyn_cast<DeclRefExpr>(E);
+  if (DRE != nullptr && isa<ParmVarDecl>(DRE->getDecl())) {
+    auto *Param = cast<ParmVarDecl>(DRE->getDecl());
+    auto *PS = Param->getAttr<PassObjectSizeAttr>();
+    return PS != nullptr && areBOSTypesCompatible(PS->getType(), Type);
+  }
+
+  llvm::APSInt Discard;
+  return E->tryEvaluateObjectSize(Discard, Context, Type);
+}
+
+ParmVarDecl *Sema::checkPassObjectSizeAttrs(FunctionDecl *Function,
+                                       ArrayRef<Expr *> Args,
+                                       OverloadCandidate *Candidate) {
+  uintptr_t ArgNo = 0;
+  for (auto I = Function->param_begin(), E = Function->param_end();
+       I != E; ++I, ++ArgNo) {
+    ParmVarDecl *Param = *I;
+    auto *Attr = Param->getAttr<PassObjectSizeAttr>();
+    if (Attr == nullptr)
+      continue;
+
+    if (Candidate != nullptr) {
+      if (Candidate->PassObjectSizeAttrs.empty())
+        Candidate->PassObjectSizeAttrs.resize(Args.size());
+      Candidate->PassObjectSizeAttrs[ArgNo] = true;
+    }
+
+    auto *Arg = Args[ArgNo]->IgnoreParenImpCasts();
+    if (!canEvaluateObjectSizeOn(Context, Arg, Attr->getType())) {
+      if (Candidate != nullptr) {
+        Candidate->Viable = false;
+        Candidate->FailureKind = ovl_fail_pass_object_size_unanswerable;
+        Candidate->DeductionFailure.Data = reinterpret_cast<char*>(Param);
+      }
+      return Param;
+    }
+  }
+
+  return nullptr;
+}
+
+static bool functionHasPassObjectSizeParams(const FunctionDecl *FD) {
+  return std::any_of(FD->param_begin(), FD->param_end(),
+                     std::mem_fn(&ParmVarDecl::hasAttr<PassObjectSizeAttr>));
+}
+
 /// A convenience routine for creating a decayed reference to a function.
 static ExprResult
 CreateFunctionRefExpr(Sema &S, FunctionDecl *Fn, NamedDecl *FoundDecl,
@@ -1062,6 +1124,26 @@
       return true;
   }
 
+  // Usage of PassObjectSizeAttr must be identical in order for us to consider
+  // two functions identical.
+  for (FunctionDecl::param_iterator
+        NewI = New->param_begin(), NewE = New->param_end(),
+        OldI = Old->param_begin(), OldE = Old->param_end();
+        NewI != NewE; ++NewI, ++OldI) {
+    assert(OldI != OldE && "Signatures should match by now");
+    auto *NewAttr = (*NewI)->getAttr<PassObjectSizeAttr>();
+    auto *OldAttr = (*OldI)->getAttr<PassObjectSizeAttr>();
+    if ((NewAttr == nullptr) != (OldAttr == nullptr))
+      return true;
+
+    if (NewAttr == nullptr)
+      continue;
+
+    assert(OldAttr != nullptr);
+    if (NewAttr->getType() != OldAttr->getType())
+      return true;
+  }
+
   // enable_if attributes are an order-sensitive part of the signature.
   for (specific_attr_iterator<EnableIfAttr>
          NewI = New->specific_attr_begin<EnableIfAttr>(),
@@ -2509,9 +2591,22 @@
   ft_parameter_mismatch,
   ft_return_type,
   ft_qualifer_mismatch,
-  ft_addr_enable_if
+  ft_addr_enable_if,
+  ft_pass_object_size
 };
 
+/// Attempts to get the FunctionProtoType from a Type. Handles
+/// MemberFunctionPointers properly.
+static const FunctionProtoType *tryGetFunctionProtoType(QualType FromType) {
+  if (auto *FPT = FromType->getAs<FunctionProtoType>())
+    return FPT;
+
+  if (auto *MPT = FromType->getAs<MemberPointerType>())
+    return MPT->getPointeeType()->getAs<FunctionProtoType>();
+
+  return nullptr;
+}
+
 /// HandleFunctionTypeMismatch - Gives diagnostic information for differeing
 /// function types.  Catches different number of parameter, mismatch in
 /// parameter types, and different return types.
@@ -2558,8 +2653,8 @@
     return;
   }
 
-  const FunctionProtoType *FromFunction = FromType->getAs<FunctionProtoType>(),
-                          *ToFunction = ToType->getAs<FunctionProtoType>();
+  const FunctionProtoType *FromFunction = tryGetFunctionProtoType(FromType),
+                          *ToFunction = tryGetFunctionProtoType(ToType);
 
   // Both types need to be function types.
   if (!FromFunction || !ToFunction) {
@@ -2597,6 +2692,12 @@
     return;
   }
 
+  if (FromFunction->hasPassObjectSizeParams() &&
+      !ToFunction->hasPassObjectSizeParams()) {
+    PDiag << ft_pass_object_size;
+    return;
+  }
+
   // Unable to find a difference, so add no extra info.
   PDiag << ft_default;
 }
@@ -5766,6 +5867,9 @@
     Candidate.DeductionFailure.Data = FailedAttr;
     return;
   }
+
+  if (checkPassObjectSizeAttrs(Function, Args, &Candidate) != nullptr)
+    return;
 }
 
 ObjCMethodDecl *Sema::SelectBestMethod(Selector Sel, MultiExprArg Args,
@@ -6156,6 +6260,9 @@
     Candidate.DeductionFailure.Data = FailedAttr;
     return;
   }
+
+  if (checkPassObjectSizeAttrs(Method, Args, &Candidate) != nullptr)
+    return;
 }
 
 /// \brief Add a C++ member function template as a candidate to the candidate
@@ -6618,6 +6725,9 @@
     Candidate.DeductionFailure.Data = FailedAttr;
     return;
   }
+
+  if (checkPassObjectSizeAttrs(Conversion, Args, &Candidate) != nullptr)
+    return;
 }
 
 /// \brief Add overload candidates for overloaded operators that are
@@ -8562,7 +8672,24 @@
            S.IdentifyCUDAPreference(Caller, Cand2.Function);
   }
 
-  return false;
+  // Tiebreaker: pass_object_size attributes
+  // Common case: No pass_object_size attributes exist. Exit.
+  bool HasPS1 = Cand1.hasPassObjectSizeAttrs();
+  bool HasPS2 = Cand2.hasPassObjectSizeAttrs();
+  if (!HasPS1 && !HasPS2)
+    return false;
+
+  // If the paramaters that pass_object_size is on in Cand1 is a strict superset
+  // of the parameters it's on in Cand2, then Cand1 wins. If this can't be
+  // phrased as a strict superset/subset relationship, it's ambiguous.
+
+  // Common case: Only one overload has pass_object_size.
+  if (HasPS1 != HasPS2)
+    return HasPS1;
+
+  const llvm::SmallBitVector &PS1 = Cand1.PassObjectSizeAttrs;
+  const llvm::SmallBitVector &PS2 = Cand2.PassObjectSizeAttrs;
+  return PS1.test(PS2) && !PS2.test(PS1);
 }
 
 /// \brief Computes the best viable function (C++ 13.3.3)
@@ -8709,6 +8836,7 @@
   OverloadCandidateKind K = ClassifyOverloadCandidate(*this, Fn, FnDesc);
   PartialDiagnostic PD = PDiag(diag::note_ovl_candidate)
                              << (unsigned) K << FnDesc;
+  // FIXME: Maybe add carrySize check here?
   if (TakingAddress && !isFunctionAlwaysEnabled(Context, Fn))
     PD << ft_addr_enable_if;
   else
@@ -9075,7 +9203,8 @@
 /// Diagnose a failed template-argument deduction.
 static void DiagnoseBadDeduction(Sema &S, Decl *Templated,
                                  DeductionFailureInfo &DeductionFailure,
-                                 unsigned NumArgs) {
+                                 unsigned NumArgs,
+                                 bool TakingCandidateAddress) {
   TemplateParameter Param = DeductionFailure.getTemplateParameter();
   NamedDecl *ParamD;
   (ParamD = Param.dyn_cast<TemplateTypeParmDecl*>()) ||
@@ -9243,6 +9372,23 @@
         }
       }
     }
+
+    // Do some hand-waving to come up with a better error message for
+    // pass_object_size cases, if possible
+    if (TakingCandidateAddress && isa<FunctionDecl>(Templated)) {
+      auto *FD = cast<FunctionDecl>(Templated);
+      unsigned NumPS =
+          std::count_if(FD->param_begin(), FD->param_end(),
+                        std::mem_fn(&ParmVarDecl::hasAttr<PassObjectSizeAttr>));
+      if (NumPS > 0) {
+        bool Plural = NumPS > 1;
+        S.Diag(Templated->getLocation(),
+               diag::note_ovl_candidate_template_has_pass_object_size)
+            << Plural;
+        return;
+      }
+    }
+
     // FIXME: For generic lambda parameters, check if the function is a lambda
     // call operator, and if so, emit a prettier and more informative 
     // diagnostic that mentions 'auto' and lambda in addition to 
@@ -9263,14 +9409,15 @@
 
 /// Diagnose a failed template-argument deduction, for function calls.
 static void DiagnoseBadDeduction(Sema &S, OverloadCandidate *Cand,
-                                 unsigned NumArgs) {
+                                 unsigned NumArgs,
+                                 bool TakingCandidateAddress) {
   unsigned TDK = Cand->DeductionFailure.Result;
   if (TDK == Sema::TDK_TooFewArguments || TDK == Sema::TDK_TooManyArguments) {
     if (CheckArityMismatch(S, Cand, NumArgs))
       return;
   }
   DiagnoseBadDeduction(S, Cand->Function, // pattern
-                       Cand->DeductionFailure, NumArgs);
+                       Cand->DeductionFailure, NumArgs, TakingCandidateAddress);
 }
 
 /// CUDA: diagnose an invalid call across targets.
@@ -9337,6 +9484,20 @@
       << Attr->getCond()->getSourceRange() << Attr->getMessage();
 }
 
+static void DiagnoseFailedPassObjectSizeAttr(Sema &S, OverloadCandidate *Cand) {
+  FunctionDecl *Callee = Cand->Function;
+  auto *Param = static_cast<ParmVarDecl*>(Cand->DeductionFailure.Data);
+  assert(Param->hasAttr<PassObjectSizeAttr>());
+
+  auto At = std::find(Callee->param_begin(), Callee->param_end(), Param);
+  assert(At != Callee->param_end());
+  // + 1 makes it 1-based
+  unsigned ParamNo = std::distance(Callee->param_begin(), At) + 1;
+  S.Diag(Callee->getLocation(),
+         diag::note_ovl_candidate_disabled_by_pass_object_size_attr)
+    << Param->getSourceRange() << ParamNo;
+}
+
 /// Generates a 'note' diagnostic for an overload candidate.  We've
 /// already generated a primary error at the call site.
 ///
@@ -9351,7 +9512,8 @@
 /// more richly for those diagnostic clients that cared, but we'd
 /// still have to be just as careful with the default diagnostics.
 static void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand,
-                                  unsigned NumArgs) {
+                                  unsigned NumArgs,
+                                  bool TakingCandidateAddress) {
   FunctionDecl *Fn = Cand->Function;
 
   // Note deleted candidates, but only if they're viable.
@@ -9379,7 +9541,7 @@
     return DiagnoseArityMismatch(S, Cand, NumArgs);
 
   case ovl_fail_bad_deduction:
-    return DiagnoseBadDeduction(S, Cand, NumArgs);
+    return DiagnoseBadDeduction(S, Cand, NumArgs, TakingCandidateAddress);
 
   case ovl_fail_illegal_constructor: {
     S.Diag(Fn->getLocation(), diag::note_ovl_candidate_illegal_constructor)
@@ -9410,6 +9572,9 @@
 
   case ovl_fail_enable_if:
     return DiagnoseFailedEnableIfAttr(S, Cand);
+
+  case ovl_fail_pass_object_size_unanswerable:
+    return DiagnoseFailedPassObjectSizeAttr(S, Cand);
   }
 }
 
@@ -9768,7 +9933,8 @@
     ++CandsShown;
 
     if (Cand->Function)
-      NoteFunctionCandidate(S, Cand, Args.size());
+      NoteFunctionCandidate(S, Cand, Args.size(),
+                            /*TakingCandidateAddress=*/false);
     else if (Cand->IsSurrogate)
       NoteSurrogateCandidate(S, Cand);
     else {
@@ -9836,9 +10002,11 @@
 /// Diagnose a template argument deduction failure.
 /// We are treating these failures as overload failures due to bad
 /// deductions.
-void TemplateSpecCandidate::NoteDeductionFailure(Sema &S) {
+void TemplateSpecCandidate::NoteDeductionFailure(Sema &S,
+                                                 bool ForTakingAddress) {
   DiagnoseBadDeduction(S, Specialization, // pattern
-                       DeductionFailure, /*NumArgs=*/0);
+                       DeductionFailure, /*NumArgs=*/0,
+                       ForTakingAddress);
 }
 
 void TemplateSpecCandidateSet::destroyCandidates() {
@@ -9891,7 +10059,7 @@
 
     assert(Cand->Specialization &&
            "Non-matching built-in candidates are not added to Cands.");
-    Cand->NoteDeductionFailure(S);
+    Cand->NoteDeductionFailure(S, ForTakingAddress);
   }
 
   if (I != E)
@@ -9956,7 +10124,7 @@
         HasComplained(false),
         OvlExprInfo(OverloadExpr::find(SourceExpr)),
         OvlExpr(OvlExprInfo.Expression),
-        FailedCandidates(OvlExpr->getNameLoc()) {
+        FailedCandidates(OvlExpr->getNameLoc(), /*ForTakingAddress=*/true) {
     ExtractUnqualifiedFunctionTypeFromTargetType();
 
     if (TargetFunctionType->isFunctionType()) {
@@ -10090,10 +10258,10 @@
     Specialization = cast<FunctionDecl>(Specialization->getCanonicalDecl());
     assert(S.isSameOrCompatibleFunctionType(
               Context.getCanonicalType(Specialization->getType()),
-              Context.getCanonicalType(TargetFunctionType)) ||
-           (!S.getLangOpts().CPlusPlus && TargetType->isVoidPointerType()));
+              Context.getCanonicalType(TargetFunctionType)));
 
-    if (!isFunctionAlwaysEnabled(S.Context, Specialization))
+    if (!isFunctionAlwaysEnabled(S.Context, Specialization) ||
+        functionHasPassObjectSizeParams(Specialization))
       return false;
 
     Matches.push_back(std::make_pair(CurAccessFunPair, Specialization));
@@ -10126,7 +10294,8 @@
         return false;
       }
 
-      if (!isFunctionAlwaysEnabled(S.Context, FunDecl))
+      if (!isFunctionAlwaysEnabled(S.Context, FunDecl) ||
+          functionHasPassObjectSizeParams(FunDecl))
         return false;
 
       QualType ResultTy;
@@ -10249,8 +10418,9 @@
            I != IEnd; ++I)
         if (FunctionDecl *Fun =
                 dyn_cast<FunctionDecl>((*I)->getUnderlyingDecl()))
-          S.NoteOverloadCandidate(Fun, TargetFunctionType,
-                                  /*TakingAddress=*/true);
+          if (!functionHasPassObjectSizeParams(Fun))
+            S.NoteOverloadCandidate(Fun, TargetFunctionType,
+                                    /*TakingAddress=*/true);
       FailedCandidates.NoteCandidates(S, OvlExpr->getLocStart());
     }
   }
@@ -11936,6 +12106,19 @@
           << Attr->getCond()->getSourceRange() << Attr->getMessage();
       return ExprError();
     }
+
+    if (ParmVarDecl *Param = checkPassObjectSizeAttrs(Method, Args)) {
+      auto Iter = std::find(Method->param_begin(), Method->param_end(), Param);
+      // + 1 makes arg count 1-based
+      unsigned ArgNo = std::distance(Method->param_begin(), Iter) + 1;
+      Diag(MemExprE->getLocStart(),
+           diag::err_ovl_no_viable_member_function_in_call)
+        << Method << Method->getSourceRange();
+      Diag(Method->getLocation(),
+           diag::note_ovl_candidate_disabled_by_pass_object_size_attr)
+        << Param->getSourceRange() << ArgNo;
+      return ExprError();
+    }
   }
 
   if ((isa<CXXConstructorDecl>(CurContext) || 
Index: lib/Sema/SemaInit.cpp
===================================================================
--- lib/Sema/SemaInit.cpp
+++ lib/Sema/SemaInit.cpp
@@ -3011,6 +3011,7 @@
   case FK_VariableLengthArrayHasInitializer:
   case FK_PlaceholderType:
   case FK_ExplicitConstructor:
+  case FK_AddressOfPassObjectSizeFunction:
     return false;
 
   case FK_ReferenceInitOverloadFailed:
@@ -4982,7 +4983,7 @@
   }
 
   assert(S.getLangOpts().CPlusPlus);
-      
+
   //     - If the destination type is a (possibly cv-qualified) class type:
   if (DestType->isRecordType()) {
     //     - If the initialization is direct-initialization, or if it is
@@ -5035,6 +5036,23 @@
     return;
   }
 
+  // There are cases in C++ where functions with pass_object_size parameters
+  // will hit this point. Try to emit a nice error if we catch them here instead
+  // of letting the implicit conversion code below emit a moderately less nice
+  // error below.
+  if (!SourceType.isNull() && SourceType->isFunctionType()) {
+    assert(Initializer != nullptr);
+    auto *DRE = dyn_cast<DeclRefExpr>(Initializer->IgnoreParenCasts());
+    if (DRE != nullptr && isa<FunctionDecl>(DRE->getDecl())) {
+      auto *FD = cast<FunctionDecl>(DRE->getDecl());
+      if (std::any_of(FD->param_begin(), FD->param_end(),
+            std::mem_fn(&ParmVarDecl::hasAttr<PassObjectSizeAttr>))) {
+        SetFailed(FK_AddressOfPassObjectSizeFunction);
+        return;
+      }
+    }
+  }
+
   //    - Otherwise, the initial value of the object being initialized is the
   //      (possibly converted) value of the initializer expression. Standard
   //      conversions (Clause 4) will be used, if necessary, to convert the
@@ -6926,6 +6944,12 @@
     break;
   }
 
+  case FK_AddressOfPassObjectSizeFunction:
+    S.Diag(Kind.getLocation(),
+           diag::err_address_of_function_with_pass_object_size_params)
+            << Args[0]->getSourceRange();
+    break;
+
   case FK_ReferenceInitOverloadFailed:
   case FK_UserConversionOverloadFailed:
     switch (FailedOverloadResult) {
@@ -7248,6 +7272,11 @@
       OS << "array requires initializer list";
       break;
 
+    case FK_AddressOfPassObjectSizeFunction:
+      OS << "functions with pass_object_size params cannot have their address "
+            "taken";
+      break;
+
     case FK_ArrayNeedsInitListOrStringLiteral:
       OS << "array requires initializer list or string literal";
       break;
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -493,6 +493,11 @@
 //  Standard Promotions and Conversions
 //===----------------------------------------------------------------------===//
 
+static bool functionHasPassObjectSizeParams(const FunctionDecl *FD) {
+  return std::any_of(FD->param_begin(), FD->param_end(),
+                     std::mem_fn(&ParmVarDecl::hasAttr<PassObjectSizeAttr>));
+}
+
 /// DefaultFunctionArrayConversion (C99 6.3.2.1p3, C99 6.3.2.1p4).
 ExprResult Sema::DefaultFunctionArrayConversion(Expr *E) {
   // Handle any placeholder expressions which made it here.
@@ -512,6 +517,17 @@
       Diag(E->getExprLoc(), diag::err_opencl_taking_function_address);
       return ExprError();
     }
+
+    if (auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts())) {
+      if (auto *FD = dyn_cast<FunctionDecl>(DRE->getDecl())) {
+        if (functionHasPassObjectSizeParams(FD)) {
+          Diag(E->getExprLoc(),
+               diag::err_address_of_function_with_pass_object_size_params);
+          return ExprError();
+        }
+      }
+    }
+
     E = ImpCastExprToType(E, Context.getPointerType(Ty),
                           CK_FunctionToPointerDecay).get();
   } else if (Ty->isArrayType()) {
@@ -4956,6 +4972,20 @@
             << Attr->getCond()->getSourceRange() << Attr->getMessage();
       }
     }
+
+    if (const ParmVarDecl *Param = checkPassObjectSizeAttrs(FD, ArgExprs)) {
+      auto Iter = std::find(FD->param_begin(), FD->param_end(), Param);
+      // Add one to bring the arguments from base zero to base one
+      unsigned ArgNo = std::distance(FD->param_begin(), Iter) + 1;
+      Diag(Fn->getLocStart(),
+           isa<CXXMethodDecl>(FD) ?
+               diag::err_ovl_no_viable_member_function_in_call :
+               diag::err_ovl_no_viable_function_in_call)
+        << FD << FD->getSourceRange();
+      Diag(FD->getLocation(),
+           diag::note_ovl_candidate_disabled_by_pass_object_size_attr)
+          << Param->getSourceRange() << ArgNo;
+    }
   }
 
   return BuildResolvedCallExpr(Fn, NDecl, LParenLoc, ArgExprs, RParenLoc,
@@ -9854,6 +9884,16 @@
     // expressions here, but the result of one is always an lvalue anyway.
   }
   ValueDecl *dcl = getPrimaryDecl(op);
+  if (auto *FD = dyn_cast_or_null<FunctionDecl>(dcl)) {
+    // It's illegal to take the address of a function with pass_object_size
+    // attributes on its parameters
+    if (functionHasPassObjectSizeParams(FD)) {
+      Diag(OpLoc, diag::err_address_of_function_with_pass_object_size_params)
+          << OrigOp.get()->getSourceRange();
+      return QualType();
+    }
+  }
+
   Expr::LValueClassification lval = op->ClassifyLValue(Context);
   unsigned AddressOfError = AO_No_Error;
 
Index: lib/Sema/SemaDeclAttr.cpp
===================================================================
--- lib/Sema/SemaDeclAttr.cpp
+++ lib/Sema/SemaDeclAttr.cpp
@@ -808,6 +808,42 @@
                           Attr.getAttributeSpellingListIndex()));
 }
 
+static void handlePassObjectSizeAttr(Sema &S, Decl *D,
+                                     const AttributeList &Attr) {
+  if (D->hasAttr<PassObjectSizeAttr>()) {
+    S.Diag(D->getLocStart(), diag::err_attribute_only_once_per_parameter)
+      << Attr.getName();
+    return;
+  }
+
+  Expr *E = Attr.getArgAsExpr(0);
+  IntegerLiteral *Int = dyn_cast<IntegerLiteral>(E->IgnoreParenCasts());
+  if (Int == nullptr) {
+    S.Diag(E->getLocStart(), diag::err_attribute_argument_type)
+      << Attr.getName() << AANT_ArgumentIntegerConstant;
+    return;
+  }
+
+  auto APType = Int->getValue();
+  if (APType.ugt(3) || APType.slt(0)) {
+    S.Diag(E->getLocStart(), diag::err_attribute_argument_out_of_bounds)
+      << Attr.getName() << 1 << E->getSourceRange();
+    return;
+  }
+
+  // pass_object_size is only supported on constant pointers
+  QualType VarType = cast<ParmVarDecl>(D)->getType();
+  if (!VarType->isPointerType() || !VarType.isConstQualified()) {
+    S.Diag(D->getLocStart(), diag::err_attribute_constant_pointers_only)
+      << Attr.getName();
+    return;
+  }
+
+  D->addAttr(::new (S.Context) PassObjectSizeAttr(
+      Attr.getRange(), S.Context, (int)APType.getSExtValue(),
+      Attr.getAttributeSpellingListIndex()));
+}
+
 static void handleConsumableAttr(Sema &S, Decl *D, const AttributeList &Attr) {
   ConsumableAttr::ConsumedState DefaultState;
 
@@ -4693,6 +4729,9 @@
   case AttributeList::AT_CUDAConstant:
     handleSimpleAttribute<CUDAConstantAttr>(S, D, Attr);
     break;
+  case AttributeList::AT_PassObjectSize:
+    handlePassObjectSizeAttr(S, D, Attr);
+    break;
   case AttributeList::AT_Constructor:
     handleConstructorAttr(S, D, Attr);
     break;
Index: lib/CodeGen/CodeGenFunction.h
===================================================================
--- lib/CodeGen/CodeGenFunction.h
+++ lib/CodeGen/CodeGenFunction.h
@@ -909,6 +909,12 @@
   /// decls.
   DeclMapTy LocalDeclMap;
 
+  /// SizeArguments - If a ParmVarDecl had the pass_object_size attribute, this
+  /// will contain a mapping from said ParmVarDecl to its implicit "object_size"
+  /// parameter.
+  llvm::SmallDenseMap<const ParmVarDecl *, const ImplicitParamDecl *, 2>
+      SizeArguments;
+
   /// Track escaped local variables with auto storage. Used during SEH
   /// outlining to produce a call to llvm.localescape.
   llvm::DenseMap<llvm::AllocaInst *, int> EscapedLocals;
@@ -3063,6 +3069,16 @@
                                   std::string &ConstraintStr,
                                   SourceLocation Loc);
 
+  /// Attempts to statically evaluate the object size of E. If that fails, emits
+  /// code to figure the size of E out for us. This is pass_object_size aware.
+  llvm::Value *evaluateOrEmitBuiltinSizeOf(const Expr *E, int Type,
+                                           llvm::Type *ResType);
+
+  /// Emits the size of E, as required by __builtin_object_size. This function
+  /// is aware of pass_object_size parameters, and will act accordingly if E is
+  /// a parameter with the pass_object_size attribute.
+  llvm::Value *emitBuiltinSizeOf(const Expr *E, int Type, llvm::Type *ResType);
+
 public:
 #ifndef NDEBUG
   // Determine whether the given argument is an Objective-C method
Index: lib/CodeGen/CodeGenFunction.cpp
===================================================================
--- lib/CodeGen/CodeGenFunction.cpp
+++ lib/CodeGen/CodeGenFunction.cpp
@@ -911,7 +911,25 @@
     CGM.getCXXABI().buildThisParam(*this, Args);
   }
 
-  Args.append(FD->param_begin(), FD->param_end());
+  auto *FPT = FD->getType()->getAs<FunctionProtoType>();
+  // Fast path -- No synthesized argument emission necessary
+  if (FPT == nullptr || !FPT->hasPassObjectSizeParams())
+    Args.append(FD->param_begin(), FD->param_end());
+  else {
+    unsigned ArgNo = 0;
+    for (auto *Param : FD->params()) {
+      Args.push_back(Param);
+      if (FPT->paramHasPassObjectSizeAttr(ArgNo)) {
+        IdentifierInfo *NoID = nullptr;
+        auto *Implicit = ImplicitParamDecl::Create(
+            getContext(), Param->getDeclContext(), Param->getLocation(),
+            NoID, getContext().getSizeType());
+        SizeArguments[Param] = Implicit;
+        Args.push_back(Implicit);
+      }
+      ++ArgNo;
+    }
+  }
 
   if (MD && (isa<CXXConstructorDecl>(MD) || isa<CXXDestructorDecl>(MD)))
     CGM.getCXXABI().addImplicitStructorParams(*this, ResTy, Args);
Index: lib/CodeGen/CGCall.cpp
===================================================================
--- lib/CodeGen/CGCall.cpp
+++ lib/CodeGen/CGCall.cpp
@@ -92,15 +92,36 @@
                                  FTNP->getExtInfo(), RequiredArgs(0));
 }
 
+/// Adds the formal paramaters in FPT to the given prefix. If any parameter in
+/// FPT has pass_object_size attrs, then we'll add parameters for those, too.
+static void appendTypesToPrefix(const CodeGenTypes &CGT,
+                                SmallVectorImpl<CanQualType> &prefix,
+                                const CanQual<FunctionProtoType> &FPT) {
+  auto *PT = FPT.getTypePtr();
+  // Fast path: No pass_object_size params.
+  if (!PT->hasPassObjectSizeParams()) {
+    prefix.append(FPT->param_type_begin(), FPT->param_type_end());
+    return;
+  }
+
+  unsigned ArgNo = 0;
+  for (auto I = FPT->param_type_begin(), E = FPT->param_type_end(); I != E;
+       ++I, ++ArgNo) {
+    prefix.push_back(*I);
+    if (PT->paramHasPassObjectSizeAttr(ArgNo))
+      prefix.push_back(CGT.getContext().getSizeType());
+  }
+}
+
 /// Arrange the LLVM function layout for a value of the given function
 /// type, on top of any implicit parameters already stored.
 static const CGFunctionInfo &
 arrangeLLVMFunctionInfo(CodeGenTypes &CGT, bool instanceMethod,
                         SmallVectorImpl<CanQualType> &prefix,
                         CanQual<FunctionProtoType> FTP) {
   RequiredArgs required = RequiredArgs::forPrototypePlus(FTP, prefix.size());
   // FIXME: Kill copy.
-  prefix.append(FTP->param_type_begin(), FTP->param_type_end());
+  appendTypesToPrefix(CGT, prefix, FTP);
   CanQualType resultType = FTP->getReturnType().getUnqualifiedType();
   return CGT.arrangeLLVMFunctionInfo(resultType, instanceMethod,
                                      /*chainCall=*/false, prefix,
@@ -208,7 +229,7 @@
   CanQual<FunctionProtoType> FTP = GetFormalType(MD);
 
   // Add the formal parameters.
-  argTypes.append(FTP->param_type_begin(), FTP->param_type_end());
+  appendTypesToPrefix(*this, argTypes, FTP);
 
   TheCXXABI.buildStructorSignature(MD, Type, argTypes);
 
@@ -2793,6 +2814,21 @@
     llvm::iterator_range<CallExpr::const_arg_iterator> ArgRange,
     const FunctionDecl *CalleeDecl, unsigned ParamsToSkip) {
   assert((int)ArgTypes.size() == (ArgRange.end() - ArgRange.begin()));
+
+  auto MaybeEmitImplicitObjectSize = [&](unsigned I, const Expr *Arg) {
+    if (CalleeDecl == nullptr || I >= CalleeDecl->getNumParams())
+      return;
+    auto *PS = CalleeDecl->getParamDecl(I)->getAttr<PassObjectSizeAttr>();
+    if (PS == nullptr)
+      return;
+
+    const auto &Context = getContext();
+    auto QT = Context.getSizeType();
+    auto T = Builder.getIntNTy(Context.getTypeSize(QT));
+    llvm::Value *V = evaluateOrEmitBuiltinSizeOf(Arg, PS->getType(), T);
+    Args.add(RValue::get(V), QT);
+  };
+
   // We *have* to evaluate arguments from right to left in the MS C++ ABI,
   // because arguments are destroyed left to right in the callee.
   if (CGM.getTarget().getCXXABI().areArgsDestroyedLeftToRightInCallee()) {
@@ -2811,6 +2847,7 @@
     for (int I = ArgTypes.size() - 1; I >= 0; --I) {
       CallExpr::const_arg_iterator Arg = ArgRange.begin() + I;
       EmitCallArg(Args, *Arg, ArgTypes[I]);
+      MaybeEmitImplicitObjectSize(I, *Arg);
       EmitNonNullArgCheck(Args.back().RV, ArgTypes[I], (*Arg)->getExprLoc(),
                           CalleeDecl, ParamsToSkip + I);
     }
@@ -2825,6 +2862,7 @@
     CallExpr::const_arg_iterator Arg = ArgRange.begin() + I;
     assert(Arg != ArgRange.end());
     EmitCallArg(Args, *Arg, ArgTypes[I]);
+    MaybeEmitImplicitObjectSize(I, *Arg);
     EmitNonNullArgCheck(Args.back().RV, ArgTypes[I], (*Arg)->getExprLoc(),
                         CalleeDecl, ParamsToSkip + I);
   }
Index: lib/CodeGen/CGBuiltin.cpp
===================================================================
--- lib/CodeGen/CGBuiltin.cpp
+++ lib/CodeGen/CGBuiltin.cpp
@@ -345,6 +345,64 @@
   return true;
 }
 
+
+llvm::Value *CodeGenFunction::evaluateOrEmitBuiltinSizeOf(const Expr *E,
+                                                          int Type,
+                                                          llvm::Type *ResType) {
+  APSInt Result;
+  if (!E->tryEvaluateObjectSize(Result, getContext(), Type))
+    return emitBuiltinSizeOf(E, Type, ResType);
+  return ConstantInt::get(ResType, Result.getZExtValue(), /*isSigned=*/true);
+}
+
+/// Checks if using the result of __builtin_object_size(p, @p From) in place of
+/// __builtin_object_size(p, @p To) is correct
+/// TODO: Duplicated in lib/Sema/SemaOverload.cpp. Maybe refactor?
+static bool areBOSTypesCompatible(int From, int To) {
+  // Note: Our __builtin_object_size implementation currently treats Type=0 and
+  // Type=2 identically. Encoding this implementation detail here may make
+  // improving __builtin_object_size difficult in the future, so it's omitted.
+  return From == To || (From == 0 && To == 1) || (From == 3 && To == 2);
+}
+
+/// Returns a Value corresponding to the size of the given expression.
+/// This Value may be either of the following:
+///   - A llvm::Argument (if E is a param with the pass_object_size attribute on
+///     it)
+///   - A call to the @llvm.objectsize intrinsic
+llvm::Value *CodeGenFunction::emitBuiltinSizeOf(const Expr *E, int Type,
+                                                llvm::Type *ResType) {
+  // We need to reference an argument if the pointer is a parameter with the
+  // pass_object_size attribute.
+  if (auto *D = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts())) {
+    auto *Param = dyn_cast<ParmVarDecl>(D->getDecl());
+    auto *PS = D->getDecl()->getAttr<PassObjectSizeAttr>();
+    if (Param != nullptr && PS != nullptr &&
+        areBOSTypesCompatible(PS->getType(), Type)) {
+      auto Iter = SizeArguments.find(Param);
+      assert(Iter != SizeArguments.end());
+
+      const ImplicitParamDecl *D = Iter->second;
+      auto DIter = LocalDeclMap.find(D);
+      assert(DIter != LocalDeclMap.end());
+
+      return EmitLoadOfScalar(DIter->second, /*volatile=*/ false,
+                              getContext().getSizeType(), E->getLocStart());
+    }
+  }
+
+  // LLVM can't always give correct results for Type=3 with @llvm.objectsize,
+  // so we can't lower this to the @llvm.objectsize intrinsic
+  if (Type == 3)
+    return ConstantInt::get(ResType, 0, /*isSigned=*/true);
+
+  auto *CI = ConstantInt::get(Builder.getInt1Ty(), (Type & 0x2) >> 1);
+  // FIXME: Get right address space.
+  llvm::Type *Tys[] = {ResType, Builder.getInt8PtrTy(0)};
+  Value *F = CGM.getIntrinsic(Intrinsic::objectsize, Tys);
+  return Builder.CreateCall(F, {EmitScalarExpr(E), CI});
+}
+
 RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD,
                                         unsigned BuiltinID, const CallExpr *E,
                                         ReturnValueSlot ReturnValue) {
@@ -602,13 +660,8 @@
     Value *Ty = EmitScalarExpr(E->getArg(1));
     ConstantInt *CI = dyn_cast<ConstantInt>(Ty);
     assert(CI);
-    uint64_t val = CI->getZExtValue();
-    CI = ConstantInt::get(Builder.getInt1Ty(), (val & 0x2) >> 1);
-    // FIXME: Get right address space.
-    llvm::Type *Tys[] = { ResType, Builder.getInt8PtrTy(0) };
-    Value *F = CGM.getIntrinsic(Intrinsic::objectsize, Tys);
     return RValue::get(
-        Builder.CreateCall(F, {EmitScalarExpr(E->getArg(0)), CI}));
+        emitBuiltinSizeOf(E->getArg(0), CI->getSExtValue(), ResType));
   }
   case Builtin::BI__builtin_prefetch: {
     Value *Locality, *RW, *Address = EmitScalarExpr(E->getArg(0));
Index: lib/AST/TypePrinter.cpp
===================================================================
--- lib/AST/TypePrinter.cpp
+++ lib/AST/TypePrinter.cpp
@@ -641,6 +641,12 @@
     for (unsigned i = 0, e = T->getNumParams(); i != e; ++i) {
       if (i) OS << ", ";
       print(T->getParamType(i), OS, StringRef());
+      if (T->paramHasPassObjectSizeAttr(i)) {
+        // We only support one pass_object_size attr per parameter, and if we're
+        // printing this for diagnostic purposes, the user really only cares
+        // that pass_object_size is there.
+        OS << " pass_object_size";
+      }
     }
   }
   
Index: lib/AST/Type.cpp
===================================================================
--- lib/AST/Type.cpp
+++ lib/AST/Type.cpp
@@ -2650,7 +2650,8 @@
       NumExceptions(epi.ExceptionSpec.Exceptions.size()),
       ExceptionSpecType(epi.ExceptionSpec.Type),
       HasAnyConsumedParams(epi.ConsumedParameters != nullptr),
-      Variadic(epi.Variadic), HasTrailingReturn(epi.HasTrailingReturn) {
+      Variadic(epi.Variadic), HasTrailingReturn(epi.HasTrailingReturn),
+      HasPassObjectSizeParams(epi.PassObjectSizeParams != nullptr) {
   assert(NumParams == params.size() && "function has too many parameters");
 
   FunctionTypeBits.TypeQuals = epi.TypeQuals;
@@ -2720,6 +2721,12 @@
     for (unsigned i = 0; i != NumParams; ++i)
       consumedParams[i] = epi.ConsumedParameters[i];
   }
+
+  if (epi.PassObjectSizeParams) {
+    const bool *epiParams = epi.PassObjectSizeParams;
+    bool *params = const_cast<bool *>(getPassObjectSizeParamsBuffer());
+    std::copy(epiParams, epiParams + NumParams, params);
+  }
 }
 
 bool FunctionProtoType::hasDependentExceptionSpec() const {
@@ -2844,6 +2851,11 @@
   }
   epi.ExtInfo.Profile(ID);
   ID.AddBoolean(epi.HasTrailingReturn);
+
+  if (epi.PassObjectSizeParams) {
+    for (unsigned I = 0; I != NumParams; ++I)
+      ID.AddBoolean(epi.PassObjectSizeParams[I]);
+  }
 }
 
 void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID,
Index: lib/AST/MicrosoftMangle.cpp
===================================================================
--- lib/AST/MicrosoftMangle.cpp
+++ lib/AST/MicrosoftMangle.cpp
@@ -268,6 +268,7 @@
   void mangleFunctionType(const FunctionType *T,
                           const FunctionDecl *D = nullptr,
                           bool ForceThisQuals = false);
+  void manglePassObjectSizeParams(const FunctionDecl *D);
   void mangleNestedName(const NamedDecl *ND);
 
 private:
@@ -1727,11 +1728,43 @@
   mangleFunctionType(T);
 }
 
+void MicrosoftCXXNameMangler::manglePassObjectSizeParams(
+    const FunctionDecl *D) {
+  // Current mangling scheme:
+  // <pass-object-size-spec-list> ::= _PS <pass-object-size-specs> _PE
+  // <pass-object-size-specs>     ::= <pass-object-size-spec>
+  //                              | <pass-object-size-spec> &
+  //                                <pass-object-size-specs>
+  // <pass-object-size-spec>      ::= <arg-no> T <type>
+  //
+  // <arg-no> is the 0-indexed argument number, <type> is the integer passed
+  // for type. If no pass_object_size attributes are present, this won't add
+  // anything.
+
+  unsigned ArgNo = 0;
+  bool EmittedPS = false;
+  for (auto *Param : D->params()) {
+    if (auto *PS = Param->getAttr<PassObjectSizeAttr>()) {
+      if (!EmittedPS) {
+        Out << "_PS";
+        EmittedPS = true;
+      } else
+        Out << '&';
+      Out << ArgNo << 'T' << PS->getType();
+    }
+    ++ArgNo;
+  }
+
+  if (EmittedPS)
+    Out << "_PE";
+}
+
 void MicrosoftCXXNameMangler::mangleFunctionType(const FunctionType *T,
                                                  const FunctionDecl *D,
                                                  bool ForceThisQuals) {
   // <function-type> ::= <this-cvr-qualifiers> <calling-convention>
-  //                     <return-type> <argument-list> <throw-spec>
+  //                     <return-type> <pass-object-size-spec-list>
+  //                     <argument-list> <throw-spec>
   const FunctionProtoType *Proto = dyn_cast<FunctionProtoType>(T);
 
   SourceRange Range;
@@ -1818,6 +1851,9 @@
     }
   }
 
+  if (D != nullptr)
+    manglePassObjectSizeParams(D);
+
   // <argument-list> ::= X # void
   //                 ::= <type>+ @
   //                 ::= <type>* Z # varargs
Index: lib/AST/ItaniumMangle.cpp
===================================================================
--- lib/AST/ItaniumMangle.cpp
+++ lib/AST/ItaniumMangle.cpp
@@ -470,7 +470,8 @@
 }
 
 void CXXNameMangler::mangleFunctionEncoding(const FunctionDecl *FD) {
-  // <encoding> ::= <function name> <bare-function-type>
+  // <encoding> ::= <function name> <pass-object-size-spec-list>
+  //                <bare-function-type>
   mangleName(FD);
 
   // Don't mangle in the type if this isn't a decl we should typically mangle.
@@ -496,6 +497,33 @@
     FunctionTypeDepth.pop(Saved);
   }
 
+  // Current mangling scheme:
+  // <pass-object-size-spec-list> ::= _PS <pass-object-size-specs> _PE
+  // <pass-object-size-specs>     ::= <pass-object-size-spec>
+  //                              | <pass-object-size-spec> &
+  //                                <pass-object-size-specs>
+  // <pass-object-size-spec>      ::= <arg-no> T <type>
+  //
+  // <arg-no> is the 0-indexed argument number, <type> is the integer passed
+  // for type. If no pass_object_size attributes are present, this won't add
+  // anything.
+  unsigned ArgNo = 0;
+  bool EmittedPS = false;
+  for (auto *Param : FD->params()) {
+    if (auto *PS = Param->getAttr<PassObjectSizeAttr>()) {
+      if (!EmittedPS) {
+        Out << "_PS";
+        EmittedPS = true;
+      } else
+        Out << '&';
+      Out << ArgNo << 'T' << PS->getType();
+    }
+    ++ArgNo;
+  }
+
+  if (EmittedPS)
+    Out << "_PE";
+
   // Whether the mangling of a function type includes the return type depends on
   // the context and the nature of the function. The rules for deciding whether
   // the return type is included are:
Index: lib/AST/ExprConstant.cpp
===================================================================
--- lib/AST/ExprConstant.cpp
+++ lib/AST/ExprConstant.cpp
@@ -1156,6 +1156,7 @@
 static bool EvaluateFloat(const Expr *E, APFloat &Result, EvalInfo &Info);
 static bool EvaluateComplex(const Expr *E, ComplexValue &Res, EvalInfo &Info);
 static bool EvaluateAtomic(const Expr *E, APValue &Result, EvalInfo &Info);
+static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result);
 
 //===----------------------------------------------------------------------===//
 // Misc utilities
@@ -6375,18 +6376,47 @@
   return false;
 }
 
-bool IntExprEvaluator::TryEvaluateBuiltinObjectSize(const CallExpr *E,
-                                                    unsigned Type) {
+/// Tries to evaluate the __builtin_object_size for @p E. If successful, returns
+/// true and stores the result in @p Size.
+///
+/// If @p WasError is non-null, this will report whether the failure to evaluate
+/// is to be treated as an Error in IntExprEvaluator.
+static bool tryEvaluateBuiltinObjectSize(const Expr *E, unsigned Type,
+                                         EvalInfo &Info, uint64_t &Size,
+                                         bool *WasError = nullptr) {
+  if (WasError != nullptr)
+    *WasError = false;
+
+  // These are here more for readability than out of laziness -- yes, this was
+  // ripped from IntExprEvaluator :)
+  auto Error = [&WasError](const Expr *E) {
+    if (WasError != nullptr)
+      *WasError = true;
+    return false;
+  };
+
+  auto Success = [&WasError, &Size](uint64_t S, const Expr *E) {
+    Size = S;
+    return true;
+  };
+
   // Determine the denoted object.
   LValue Base;
   {
     // The operand of __builtin_object_size is never evaluated for side-effects.
     // If there are any, but we can determine the pointed-to object anyway, then
     // ignore the side-effects.
     SpeculativeEvaluationRAII SpeculativeEval(Info);
     FoldOffsetRAII Fold(Info, Type & 1);
-    const Expr *Ptr = ignorePointerCastsAndParens(E->getArg(0));
-    if (!EvaluatePointer(Ptr, Base, Info))
+
+    if (E->isGLValue()) {
+      // It's possible for us to be given GLValues if we're called via
+      // Expr::tryEvaluateObjectSize.
+      APValue RVal;
+      if (!EvaluateAsRValue(Info, E, RVal))
+        return false;
+      Base.setFrom(Info.Ctx, RVal);
+    } else if (!EvaluatePointer(ignorePointerCastsAndParens(E), Base, Info))
       return false;
   }
 
@@ -6482,7 +6512,18 @@
   if (BaseOffset > EndOffset)
     return Success(0, E);
 
-  return Success(EndOffset - BaseOffset, E);
+  return Success((EndOffset - BaseOffset).getQuantity(), E);
+}
+
+bool IntExprEvaluator::TryEvaluateBuiltinObjectSize(const CallExpr *E,
+                                                    unsigned Type) {
+  uint64_t Size;
+  bool WasError;
+  if(::tryEvaluateBuiltinObjectSize(E->getArg(0), Type, Info, Size, &WasError))
+    return Success(Size, E);
+  if (WasError)
+    return Error(E);
+  return false;
 }
 
 bool IntExprEvaluator::VisitCallExpr(const CallExpr *E) {
@@ -6502,9 +6543,7 @@
     // If evaluating the argument has side-effects, we can't determine the size
     // of the object, and so we lower it to unknown now. CodeGen relies on us to
     // handle all cases where the expression has side-effects.
-    // Likewise, if Type is 3, we must handle this because CodeGen cannot give a
-    // conservatively correct answer in that case.
-    if (E->getArg(0)->HasSideEffects(Info.Ctx) || Type == 3)
+    if (E->getArg(0)->HasSideEffects(Info.Ctx))
       return Success((Type & 2) ? 0 : -1, E);
 
     // Expression had no side effects, but we couldn't statically determine the
@@ -9477,3 +9516,20 @@
   Evaluate(ResultScratch, Info, E);
   return Diags.empty();
 }
+
+bool Expr::tryEvaluateObjectSize(llvm::APSInt &Result, ASTContext &Ctx,
+                                 unsigned Type) const {
+  if (!getType()->isPointerType() &&
+      !getType()->canDecayToPointerType())
+    return false;
+
+  Expr::EvalStatus Status;
+  EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantFold);
+  uint64_t Size;
+
+  if (!::tryEvaluateBuiltinObjectSize(this, Type, Info, Size))
+    return false;
+
+  Result = llvm::APInt(/*NumBits=*/64, Size, /*IsSigned=*/true);
+  return true;
+}
Index: lib/AST/ASTContext.cpp
===================================================================
--- lib/AST/ASTContext.cpp
+++ lib/AST/ASTContext.cpp
@@ -3073,6 +3073,8 @@
   }
   if (EPI.ConsumedParameters)
     Size += NumArgs * sizeof(bool);
+  if (EPI.PassObjectSizeParams)
+    Size += NumArgs * sizeof(bool);
 
   FunctionProtoType *FTP = (FunctionProtoType*) Allocate(Size, TypeAlignment);
   FunctionProtoType::ExtProtoInfo newEPI = EPI;
Index: include/clang/Sema/TemplateDeduction.h
===================================================================
--- include/clang/Sema/TemplateDeduction.h
+++ include/clang/Sema/TemplateDeduction.h
@@ -236,7 +236,7 @@
   }
 
   /// Diagnose a template argument deduction failure.
-  void NoteDeductionFailure(Sema &S);
+  void NoteDeductionFailure(Sema &S, bool ForTakingAddress);
 };
 
 /// TemplateSpecCandidateSet - A set of generalized overload candidates,
@@ -246,15 +246,20 @@
 class TemplateSpecCandidateSet {
   SmallVector<TemplateSpecCandidate, 16> Candidates;
   SourceLocation Loc;
+  // Stores whether we're taking the address of these candidates. This helps us
+  // produce better error messages when dealing with the pass_object_size
+  // attribute on parameters.
+  bool ForTakingAddress;
 
   TemplateSpecCandidateSet(
       const TemplateSpecCandidateSet &) = delete;
   void operator=(const TemplateSpecCandidateSet &) = delete;
 
   void destroyCandidates();
 
 public:
-  TemplateSpecCandidateSet(SourceLocation Loc) : Loc(Loc) {}
+  TemplateSpecCandidateSet(SourceLocation Loc, bool ForTakingAddress=false)
+      : Loc(Loc), ForTakingAddress(ForTakingAddress) {}
   ~TemplateSpecCandidateSet() { destroyCandidates(); }
 
   SourceLocation getLocation() const { return Loc; }
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -145,6 +145,7 @@
   class ObjCProtocolDecl;
   class OMPThreadPrivateDecl;
   class OMPClause;
+  struct OverloadCandidate;
   class OverloadCandidateSet;
   class OverloadExpr;
   class ParenListExpr;
@@ -2453,6 +2454,17 @@
   EnableIfAttr *CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr *> Args,
                               bool MissingImplicitThis = false);
 
+  /// Check to be sure that all pass_object_size attributes in a FunctionDecl
+  /// can be answered. Returns the first ParmVarDecl that we failed to evaluate
+  /// pass_object_size on, or nullptr if no issues were had.
+  ///
+  /// If we're given an OverloadCandidate, this will "fill in" the
+  /// PassObjectSizeAttrs field of it, and set error reporting information if
+  /// evaluating the size of a parameter with pass_object_size fails.
+  ParmVarDecl *checkPassObjectSizeAttrs(FunctionDecl *Function,
+                                        ArrayRef<Expr *> Args,
+                                        OverloadCandidate *Candidate = nullptr);
+
   // [PossiblyAFunctionType]  -->   [Return]
   // NonFunctionType --> NonFunctionType
   // R (A) --> R(A)
Index: include/clang/Sema/Overload.h
===================================================================
--- include/clang/Sema/Overload.h
+++ include/clang/Sema/Overload.h
@@ -23,6 +23,7 @@
 #include "clang/AST/UnresolvedSet.h"
 #include "clang/Sema/SemaFixItUtils.h"
 #include "clang/Sema/TemplateDeduction.h"
+#include "llvm/ADT/SmallBitVector.h"
 #include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/Support/AlignOf.h"
@@ -582,11 +583,20 @@
 
     /// This candidate function was not viable because an enable_if
     /// attribute disabled it.
-    ovl_fail_enable_if
+    ovl_fail_enable_if,
+
+    /// This candidate function was not viable because the size of a parameter
+    /// with the pass_object_size attribute could not be determined
+    ovl_fail_pass_object_size_unanswerable
   };
 
   /// OverloadCandidate - A single candidate in an overload set (C++ 13.3).
   struct OverloadCandidate {
+    /// The pass_object_size attrs on the OverloadCandidate. If none exist, then
+    /// this field will be empty, regardless of the number of parameters that
+    /// the function has.
+    llvm::SmallBitVector PassObjectSizeAttrs;
+
     /// Function - The actual function that this candidate
     /// represents. When NULL, this is a built-in candidate
     /// (C++ [over.oper]) or a surrogate for a conversion to a
@@ -679,6 +689,8 @@
       return CanFix;
     }
 
+    bool hasPassObjectSizeAttrs() const { return !PassObjectSizeAttrs.empty(); }
+
     unsigned getNumParams() const {
       if (IsSurrogate) {
         auto STy = Surrogate->getConversionType();
Index: include/clang/Sema/Initialization.h
===================================================================
--- include/clang/Sema/Initialization.h
+++ include/clang/Sema/Initialization.h
@@ -828,6 +828,9 @@
     /// \brief Initializer has a placeholder type which cannot be
     /// resolved by initialization.
     FK_PlaceholderType,
+    /// \brief Attempted to take the address of a function with pass_object_size
+    /// on one or more of its parameters
+    FK_AddressOfPassObjectSizeFunction,
     /// \brief List-copy-initialization chose an explicit constructor.
     FK_ExplicitConstructor
   };
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -1501,7 +1501,8 @@
   "volatile and restrict|const, volatile, and restrict}5 vs "
   "%select{none|const|restrict|const and restrict|volatile|const and volatile|"
   "volatile and restrict|const, volatile, and restrict}6)"
-  "|: cannot take the address of a potentially disabled function}4">;
+  "|: cannot take the address of a potentially disabled function"
+  "|: mismatch between parameters with pass_object_size attributes}4">;
 
 def err_lvalue_to_rvalue_ref : Error<"rvalue reference %diff{to type $ cannot "
   "bind to lvalue of type $|cannot bind to incompatible lvalue}0,1">;
@@ -2056,12 +2057,16 @@
   "invalid attribute argument %0 - expecting a vector or vectorizable scalar type">;
 def err_attribute_argument_out_of_bounds : Error<
   "%0 attribute parameter %1 is out of bounds">;
+def err_attribute_only_once_per_parameter : Error<
+  "%0 attribute can only be applied once per parameter">;
 def err_attribute_uuid_malformed_guid : Error<
   "uuid attribute contains a malformed GUID">;
 def warn_attribute_pointers_only : Warning<
   "%0 attribute only applies to pointer arguments">,
   InGroup<IgnoredAttributes>;
 def err_attribute_pointers_only : Error<warn_attribute_pointers_only.Text>;
+def err_attribute_constant_pointers_only : Error<
+  "%0 attribute only applies to constant pointer arguments">;
 def warn_attribute_return_pointers_only : Warning<
   "%0 attribute only applies to return values that are pointers">,
   InGroup<IgnoredAttributes>;
@@ -2973,7 +2978,8 @@
     "|volatile and restrict|const, volatile, and restrict}3 but found "
     "%select{none|const|restrict|const and restrict|volatile|const and volatile"
     "|volatile and restrict|const, volatile, and restrict}4)"
-    "| made ineligible by enable_if}2">;
+    "| made ineligible by enable_if"
+    "| made ineligible by pass_object_size attributes}2">;
 
 def note_ovl_candidate_inherited_constructor : Note<"inherited from here">;
 def note_ovl_candidate_illegal_constructor : Note<
@@ -3004,6 +3010,8 @@
     "candidate template ignored: disabled by %0%1">;
 def note_ovl_candidate_disabled_by_enable_if_attr : Note<
     "candidate disabled: %0">;
+def note_ovl_candidate_disabled_by_pass_object_size_attr : Note<
+    "cannot statically determine the size of parameter %0">;
 def note_ovl_candidate_failed_overload_resolution : Note<
     "candidate template ignored: couldn't resolve reference to overloaded "
     "function %0">;
@@ -3014,6 +3022,9 @@
 // can handle that case properly.
 def note_ovl_candidate_non_deduced_mismatch_qualified : Note<
     "candidate template ignored: could not match %q0 against %q1">;
+def note_ovl_candidate_template_has_pass_object_size : Note<
+    "candidate template is ineligible because pass_object_size"
+    " attribute%select{ is|s are}0 present">;
     
 // Note that we don't treat templates differently for this diagnostic.
 def note_ovl_candidate_arity : Note<"candidate "
@@ -5086,6 +5097,8 @@
   "must explicitly qualify name of member function when taking its address">;
 def err_invalid_form_pointer_member_function : Error<
   "cannot create a non-constant pointer to member function">;
+def err_address_of_function_with_pass_object_size_params: Error<
+  "functions with pass_object_size params cannot have their address taken">;
 def err_parens_pointer_member_function : Error<
   "cannot parenthesize the name of a method when forming a member pointer">;
 def err_typecheck_invalid_lvalue_addrof_addrof_function : Error<
@@ -5669,7 +5682,8 @@
   "volatile and restrict|const, volatile, and restrict}2 vs "
   "%select{none|const|restrict|const and restrict|volatile|const and volatile|"
   "volatile and restrict|const, volatile, and restrict}3)"
-  "|: mismatch in enable_if attributes}1">;
+  "|: mismatch in enable_if attributes"
+  "|: made ineligible by pass_object_size attributes}1">;
 def warn_using_directive_in_header : Warning<
   "using namespace directive in global context in header">,
   InGroup<HeaderHygiene>, DefaultIgnore;
@@ -5907,7 +5921,8 @@
   "volatile and restrict|const, volatile, and restrict}5 vs "
   "%select{none|const|restrict|const and restrict|volatile|const and volatile|"
   "volatile and restrict|const, volatile, and restrict}6)"
-  "|: cannot take the address of a potentially disabled function}4">;
+  "|: cannot take the address of a potentially disabled function"
+  "|: mismatch between parameters with pass_object_size attributes}4">;
 def err_typecheck_missing_return_type_incompatible : Error<
   "%diff{return type $ must match previous return type $|"
   "return type must match previous return type}0,1 when %select{block "
Index: include/clang/Basic/AttrDocs.td
===================================================================
--- include/clang/Basic/AttrDocs.td
+++ include/clang/Basic/AttrDocs.td
@@ -263,6 +263,108 @@
   }];
 }
 
+def PassObjectSizeDocs : Documentation {
+  let Category = DocCatVariable; // Technically it's a parameter doc, but eh.
+  let Content = [{
+.. Note:: The mangling of functions with parameters that are annotated with
+  ``pass_object_size`` is not yet standardized.
+
+The ``pass_object_size(Type)`` attribute can be placed on function parameters to
+instruct clang to call ``__builtin_object_size(param, Type)`` at each callsite
+of said function, and implicitly pass the result of this call in as an invisible
+argument of type ``size_t`` as the parameter directly after the parameter
+annotated with ``pass_object_size``. Clang will also replace any calls to
+``__builtin_object_size(param, Type)`` in the function by said implicit
+parameter.
+
+Example usage:
+
+.. code-block:: c
+
+  int bzero1(char *const p __attribute__((pass_object_size(0))))
+      __attribute__((noinline)) {
+    int i = 0;
+    for (/**/; i < __builtin_object_size(p, 0); ++i) {
+      p[i] = 0;
+    }
+    return i;
+  }
+
+  int main() {
+    char chars[100];
+    int n = bzero1(&chars[0]);
+    assert(n == sizeof(chars));
+  }
+
+If successfully evaluating ``__builtin_object_size(param, Type)`` at the
+callsite is not possible, then the function with ``pass_object_size(Type)`` on
+its parameter is considered to be disabled. This implies that, given the
+definition of ``bzero1`` above, the following code will fail to compile:
+
+.. code-block:: c
+
+  int main() {
+    char *unknown;
+    // fails with "cannot statically determine the size of parameter 1"
+    bzero1(unknown);
+  }
+
+``pass_object_size(Type)`` is considered in overload resolution. As stated
+above, if the evaluation of ``__builtin_object_size(param, Type))`` is not
+possible at the callsite, then the candidate with ``pass_object_size`` is not
+viable. Beyond this, we consider viable candidates with ``pass_object_size`` on
+some parameter as better matches than other candidates that lack
+``pass_object_size`` on said parameter. More concretely:
+
+.. code-block:: c++
+
+  #define PS(N) __attribute__((pass_object_size(N)))
+  void Foo(void *const a, void *const b); // Overload A
+  void Foo(void *const a PS(0), void *const b); // Overload B
+  void Foo(void *const a, void *const b PS(0)); // Overload C
+  void Foo(void *const a PS(0), void *const b PS(0)); // Overload D
+
+  void Bar(void *const a PS(0), void *const b); // Overload E
+  void Bar(void *const a, void *const b PS(0)); // Overload F
+
+  void main() {
+    char known[10], *unknown;
+    Foo(unknown, unknown); // Calls Overload A
+    Foo(known, unknown); // Calls Overload B
+    Foo(unknown, known); // Calls Overload C
+    Foo(known, known); // Calls Overload D
+
+    Bar(unknown, unknown); // Ambiguous -- no candidates
+    Bar(known, unknown); // Calls Overload E
+    Bar(unknown, known); // Calls Overload F
+    Bar(known, known); // Ambiguous -- candidate overloads: E, F
+  }
+
+Currently, ``pass_object_size`` is a bit restricted in terms of its usage:
+
+* Only one use of pass_object_size is allowed per parameter.
+
+* It is an error to take the address of a function with pass_object_size on any
+  of its parameters. If you wish to do this, you can create an overload without
+  pass_object_size on any parameters.
+
+* It is an error to apply the ``pass_object_size`` attribute on parameters that
+  are not const pointers
+
+* Because the size is passed at runtime, ``CallFoo`` below will always call
+  A:
+
+  .. code-block:: c++
+
+    int Foo(int n) __attribute__((enable_if(n > 0, ""))); // function A
+    int Foo(int n) __attribute__((enable_if(n <= 0, ""))); // function B
+    int CallFoo(const void *p __attribute__((pass_object_size(0))))
+        __attribute__((noinline)) {
+      return Foo(__builtin_object_size(p, 0));
+    }
+  }];
+}
+
 def OverloadableDocs : Documentation {
   let Category = DocCatFunction;
   let Content = [{
Index: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td
+++ include/clang/Basic/Attr.td
@@ -980,6 +980,15 @@
   let Documentation = [ReturnsNonNullDocs];
 }
 
+// pass_object_size(N) indicates that the parameter should have
+// __builtin_object_size with Type=N evaluated on the parameter at the callsite.
+def PassObjectSize : InheritableParamAttr {
+  let Spellings = [GNU<"pass_object_size">];
+  let Args = [IntArgument<"Type">];
+  let Subjects = SubjectList<[ParmVar]>;
+  let Documentation = [PassObjectSizeDocs];
+}
+
 // Nullability type attributes.
 def TypeNonNull : TypeAttr {
   let Spellings = [Keyword<"_Nonnull">];
Index: include/clang/AST/Type.h
===================================================================
--- include/clang/AST/Type.h
+++ include/clang/AST/Type.h
@@ -3050,11 +3050,13 @@
   struct ExtProtoInfo {
     ExtProtoInfo()
         : Variadic(false), HasTrailingReturn(false), TypeQuals(0),
-          RefQualifier(RQ_None), ConsumedParameters(nullptr) {}
+          RefQualifier(RQ_None), ConsumedParameters(nullptr),
+          PassObjectSizeParams(nullptr) {}
 
     ExtProtoInfo(CallingConv CC)
         : ExtInfo(CC), Variadic(false), HasTrailingReturn(false), TypeQuals(0),
-          RefQualifier(RQ_None), ConsumedParameters(nullptr) {}
+          RefQualifier(RQ_None), ConsumedParameters(nullptr),
+          PassObjectSizeParams(nullptr) {}
 
     ExtProtoInfo withExceptionSpec(const ExceptionSpecInfo &O) {
       ExtProtoInfo Result(*this);
@@ -3069,6 +3071,7 @@
     RefQualifierKind RefQualifier;
     ExceptionSpecInfo ExceptionSpec;
     const bool *ConsumedParameters;
+    const bool *PassObjectSizeParams;
   };
 
 private:
@@ -3104,6 +3107,9 @@
   /// Whether this function has a trailing return type.
   unsigned HasTrailingReturn : 1;
 
+  /// Whether this function has pass_object_size attribute(s) on its parameters
+  unsigned HasPassObjectSizeParams : 1;
+
   // ParamInfo - There is an variable size array after the class in memory that
   // holds the parameter types.
 
@@ -3122,21 +3128,34 @@
   // and of length NumParams, holding flags indicating which parameters
   // are consumed.  This only appears if HasAnyConsumedParams is true.
 
-  friend class ASTContext;  // ASTContext creates these.
+  // PassObjectSizeParams - A variable size array, following ConsumedParameters
+  // and of length NumParams, holding flags indicating which parameters have the
+  // pass_object_size attribute. This only appears if HasPassObjectSizeParams is
+  // true.
 
-  const bool *getConsumedParamsBuffer() const {
-    assert(hasAnyConsumedParams());
+  friend class ASTContext;  // ASTContext creates these.
 
-    // Find the end of the exceptions.
+  const void *getExceptionSpecBufferEnd() const {
     Expr *const *eh_end = reinterpret_cast<Expr *const *>(exception_end());
     if (getExceptionSpecType() == EST_ComputedNoexcept)
       eh_end += 1; // NoexceptExpr
-    // The memory layout of these types isn't handled here, so
-    // hopefully this is never called for them?
-    assert(getExceptionSpecType() != EST_Uninstantiated &&
-           getExceptionSpecType() != EST_Unevaluated);
+    else if (getExceptionSpecType() == EST_Unevaluated)
+      eh_end += 1; // ExceptionSpecDecl
+    else if (getExceptionSpecType() == EST_Uninstantiated)
+      eh_end += 2; // ExceptionSpecDecl + ExceptionSpecTemplate
+    return eh_end;
+  }
+
+  const bool *getConsumedParamsBuffer() const {
+    assert(hasAnyConsumedParams());
+    return reinterpret_cast<const bool*>(getExceptionSpecBufferEnd());
+  }
 
-    return reinterpret_cast<const bool*>(eh_end);
+  const bool *getPassObjectSizeParamsBuffer() const {
+    assert(hasPassObjectSizeParams());
+    if (hasAnyConsumedParams())
+      return getConsumedParamsBuffer() + getNumParams();
+    return reinterpret_cast<const bool*>(getExceptionSpecBufferEnd());
   }
 
 public:
@@ -3169,6 +3188,8 @@
     }
     if (hasAnyConsumedParams())
       EPI.ConsumedParameters = getConsumedParamsBuffer();
+    if (hasPassObjectSizeParams())
+      EPI.PassObjectSizeParams = getPassObjectSizeParamsBuffer();
     return EPI;
   }
 
@@ -3291,6 +3312,12 @@
     return false;
   }
 
+  bool hasPassObjectSizeParams() const { return HasPassObjectSizeParams; }
+  bool paramHasPassObjectSizeAttr(unsigned I) const {
+    assert(I < getNumParams() && "parameter index out of range");
+    return hasPassObjectSizeParams() && getPassObjectSizeParamsBuffer()[I];
+  }
+
   bool isSugared() const { return false; }
   QualType desugar() const { return QualType(this, 0); }
 
Index: include/clang/AST/Expr.h
===================================================================
--- include/clang/AST/Expr.h
+++ include/clang/AST/Expr.h
@@ -628,6 +628,12 @@
                                 const FunctionDecl *Callee,
                                 ArrayRef<const Expr*> Args) const;
 
+  /// tryEvaluateObjectSize - If the current Expr is a pointer, this will try to
+  /// statically determine how many bytes remain in the object this pointer is
+  /// pointing to.
+  bool tryEvaluateObjectSize(llvm::APSInt &Result, ASTContext &Ctx,
+                             unsigned Type) const;
+
   /// \brief Enumeration used to describe the kind of Null pointer constant
   /// returned from \c isNullPointerConstant().
   enum NullPointerConstantKind {
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to