george.burgess.iv created this revision. george.burgess.iv added a reviewer: rsmith. george.burgess.iv added subscribers: cfe-commits, rsmith.
This was a fun patch :) This patch adds the parameter attribute `pass_object_size` to Clang. The design doc for this attribute is available here: https://docs.google.com/document/d/14TwNLxFL_jhiYn7_kppNUDiaTopRAIY5bc-B8NcnKMM/edit?usp=sharing General notes: - Never mangled things before, so I'm open to any suggestions on how to make the mangling for pass_object_size better :) - There's a tiny diagnostics bugfix in here -- I'm happy to make that its own patch. (See: `tryGetFunctionProtoType` in SemaOverload.cpp) - A lot of the additions to Sema (anything that's a boolean named like `TakingCandidateAddress`) are to improve diagnostics on templated functions with pass_object_size on their parameters. I'm happy to simplify the diff a bit if we think that the improved diag ("functions with pass_object_size params cannot have their address taken" vs "assigning to 'int (*)(void *)' from incompatible type 'int (void *const pass_object_size)'") isn't worth the extra cruft. @rsmith: Other than the name change, the design has changed (since the last time we talked) such that having `pass_object_size(N)` on some parameter P makes the function act like there's an enable_if that disables the function if we can't determine the object size of P at the callsite. More concretely: - One can no longer take the address of a function with `pass_object_size` - If an overload candidate parameter with `pass_object_size` on it cannot have its object size determined, then the candidate is not viable. - If there's no overloading going on at all (e.g. in C), it's still an error to call a function with `pass_object_size` on one or more of its params if the object size of that param can't be determined. 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 @@ -3920,6 +3920,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(); @@ -3982,12 +3986,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); DeclContext *FDLookupContext = FD->getDeclContext()->getRedeclContext(); for (LookupResult::iterator I = Previous.begin(), E = Previous.end(); 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, @@ -1056,6 +1118,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>(), @@ -2488,9 +2570,22 @@ ft_parameter_arity, ft_parameter_mismatch, ft_return_type, - ft_qualifer_mismatch + ft_qualifer_mismatch, + 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. @@ -2537,8 +2632,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) { @@ -2576,6 +2671,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; } @@ -5744,6 +5845,9 @@ Candidate.DeductionFailure.Data = FailedAttr; return; } + + if (checkPassObjectSizeAttrs(Function, Args, &Candidate) != nullptr) + return; } ObjCMethodDecl *Sema::SelectBestMethod(Selector Sel, MultiExprArg Args, @@ -6127,6 +6231,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 @@ -6589,6 +6696,9 @@ Candidate.DeductionFailure.Data = FailedAttr; return; } + + if (checkPassObjectSizeAttrs(Conversion, Args, &Candidate) != nullptr) + return; } /// \brief Add overload candidates for overloaded operators that are @@ -8534,7 +8644,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) @@ -9029,7 +9156,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*>()) || @@ -9197,6 +9325,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 @@ -9217,14 +9362,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. @@ -9291,6 +9437,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. /// @@ -9305,7 +9465,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. @@ -9333,7 +9494,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) @@ -9364,6 +9525,9 @@ case ovl_fail_enable_if: return DiagnoseFailedEnableIfAttr(S, Cand); + + case ovl_fail_pass_object_size_unanswerable: + return DiagnoseFailedPassObjectSizeAttr(S, Cand); } } @@ -9722,7 +9886,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 { @@ -9790,9 +9955,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() { @@ -9845,7 +10012,7 @@ assert(Cand->Specialization && "Non-matching built-in candidates are not added to Cands."); - Cand->NoteDeductionFailure(S); + Cand->NoteDeductionFailure(S, ForTakingAddress); } if (I != E) @@ -9908,7 +10075,7 @@ StaticMemberFunctionFromBoundPointer(false), OvlExprInfo(OverloadExpr::find(SourceExpr)), OvlExpr(OvlExprInfo.Expression), - FailedCandidates(OvlExpr->getNameLoc()) { + FailedCandidates(OvlExpr->getNameLoc(), /*ForTakingAddress=*/true) { ExtractUnqualifiedFunctionTypeFromTargetType(); if (TargetFunctionType->isFunctionType()) { @@ -10011,6 +10178,9 @@ assert(S.isSameOrCompatibleFunctionType( Context.getCanonicalType(Specialization->getType()), Context.getCanonicalType(TargetFunctionType))); + // Never allow a pass_object_size function to have its address taken + if (functionHasPassObjectSizeParams(Specialization)) + return false; Matches.push_back(std::make_pair(CurAccessFunPair, Specialization)); return true; } @@ -10039,6 +10209,11 @@ S.DeduceReturnType(FunDecl, SourceExpr->getLocStart(), Complain)) return false; + // Functions with pass_object_size on their params are never allowed to + // have their address taken. + if (functionHasPassObjectSizeParams(FunDecl)) + return false; + QualType ResultTy; if (Context.hasSameUnqualifiedType(TargetFunctionType, FunDecl->getType()) || @@ -10156,7 +10331,8 @@ I != IEnd; ++I) if (FunctionDecl *Fun = dyn_cast<FunctionDecl>((*I)->getUnderlyingDecl())) - S.NoteOverloadCandidate(Fun, TargetFunctionType); + if (!functionHasPassObjectSizeParams(Fun)) + S.NoteOverloadCandidate(Fun, TargetFunctionType); FailedCandidates.NoteCandidates(S, OvlExpr->getLocStart()); } } @@ -11840,6 +12016,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 @@ -6924,6 +6942,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) { @@ -7246,6 +7270,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()) { @@ -5012,6 +5028,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, @@ -9887,6 +9917,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 @@ -811,6 +811,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; @@ -4673,6 +4709,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 @@ -913,6 +913,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; @@ -3100,6 +3106,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 @@ -91,15 +91,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, @@ -207,7 +228,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); @@ -2805,6 +2826,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()) { @@ -2823,6 +2859,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); } @@ -2837,6 +2874,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 @@ -288,6 +288,63 @@ return Builder.CreateCall(CGM.getIntrinsic(inst), ArgValue); } +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) { @@ -545,13 +602,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 @@ -1137,6 +1137,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 @@ -6276,18 +6277,47 @@ return ignorePointerCastsAndParens(SubExpr); } -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; } @@ -6366,7 +6396,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) { @@ -6386,9 +6427,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 @@ -9361,3 +9400,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 @@ -3064,6 +3064,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; @@ -2434,18 +2435,29 @@ OverloadCandidateSet& CandidateSet, bool PartialOverloading = false); - // Emit as a 'note' the specific overload candidate + /// Emit as a 'note' the specific overload candidate void NoteOverloadCandidate(FunctionDecl *Fn, QualType DestType = QualType()); - // Emit as a series of 'note's all template and non-templates - // identified by the expression Expr + /// Emit as a series of 'note's all template and non-templates + /// identified by the expression Expr void NoteAllOverloadCandidates(Expr* E, QualType DestType = QualType()); /// Check the enable_if expressions on the given function. Returns the first /// failing attribute, or NULL if they were all successful. 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" @@ -579,11 +580,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 @@ -676,6 +686,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 @@ -1493,7 +1493,8 @@ "%select{none|const|restrict|const and restrict|volatile|const and volatile|" "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)}4">; + "volatile and restrict|const, volatile, and restrict}6)" + "|: 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">; @@ -2048,12 +2049,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>; @@ -2960,7 +2965,9 @@ "%select{none|const|restrict|const and restrict|volatile|const and volatile" "|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)}2">; + "|volatile and restrict|const, volatile, and restrict}4)" + "| 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< @@ -2991,6 +2998,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">; @@ -3001,6 +3010,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 " @@ -5073,6 +5085,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< @@ -5655,7 +5669,8 @@ "%select{none|const|restrict|const and restrict|volatile|const and volatile|" "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)}1">; + "volatile and restrict|const, volatile, and restrict}3)" + "|: 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; @@ -5892,7 +5907,8 @@ "%select{none|const|restrict|const and restrict|volatile|const and volatile|" "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)}4">; + "volatile and restrict|const, volatile, and restrict}6)" + "|: 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 @@ -629,6 +629,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