Index: include/clang/Basic/Builtins.def =================================================================== --- include/clang/Basic/Builtins.def +++ include/clang/Basic/Builtins.def @@ -1374,6 +1374,7 @@ BUILTIN(__builtin_operator_new, "v*z", "c") BUILTIN(__builtin_operator_delete, "vv*", "n") BUILTIN(__builtin_char_memchr, "c*cC*iz", "n") +BUILTIN(__builtin_dump_struct, "ivC*v*", "tn") // Safestack builtins BUILTIN(__builtin___get_unsafe_stack_start, "v*", "Fn") Index: lib/CodeGen/CGBuiltin.cpp =================================================================== --- lib/CodeGen/CGBuiltin.cpp +++ lib/CodeGen/CGBuiltin.cpp @@ -14,6 +14,7 @@ #include "CGCXXABI.h" #include "CGObjCRuntime.h" #include "CGOpenCLRuntime.h" +#include "CGRecordLayout.h" #include "CodeGenFunction.h" #include "CodeGenModule.h" #include "ConstantEmitter.h" @@ -930,6 +931,93 @@ return RValue::get(Overflow); } +static llvm::Value *dumpRecord(CodeGenFunction &CGF, QualType RType, + Value *&RecordPtr, CharUnits Align, Value *Func, + int Lvl) { + const auto *RT = RType->getAs(); + ASTContext &Context = CGF.getContext(); + RecordDecl *RD = RT->getDecl()->getDefinition(); + ASTContext &Ctx = RD->getASTContext(); + const ASTRecordLayout &RL = Ctx.getASTRecordLayout(RD); + std::string Pad = std::string(Lvl * 4, ' '); + + Value *GString = + CGF.Builder.CreateGlobalStringPtr(RType.getAsString() + " {\n"); + Value *Res = CGF.Builder.CreateCall(Func, {GString}); + + static llvm::DenseMap Types; + if (Types.empty()) { + Types[Context.CharTy] = "%c"; + Types[Context.BoolTy] = "%d"; + Types[Context.IntTy] = "%d"; + Types[Context.UnsignedIntTy] = "%u"; + Types[Context.LongTy] = "%ld"; + Types[Context.UnsignedLongTy] = "%lu"; + Types[Context.LongLongTy] = "%lld"; + Types[Context.UnsignedLongLongTy] = "%llu"; + Types[Context.ShortTy] = "%hd"; + Types[Context.UnsignedShortTy] = "%hu"; + Types[Context.VoidPtrTy] = "%p"; + Types[Context.FloatTy] = "%f"; + Types[Context.DoubleTy] = "%f"; + Types[Context.LongDoubleTy] = "%Lf"; + Types[Context.getPointerType(Context.CharTy)] = "%s"; + } + + for (const auto *FD : RD->fields()) { + uint64_t Off = RL.getFieldOffset(FD->getFieldIndex()); + Off = Ctx.toCharUnitsFromBits(Off).getQuantity(); + + Value *FieldPtr = RecordPtr; + if (RD->isUnion()) + FieldPtr = CGF.Builder.CreatePointerCast( + FieldPtr, CGF.ConvertType(Context.getPointerType(FD->getType()))); + else + FieldPtr = CGF.Builder.CreateStructGEP(CGF.ConvertType(RType), FieldPtr, + FD->getFieldIndex()); + + GString = CGF.Builder.CreateGlobalStringPtr( + llvm::Twine(Pad) + .concat(FD->getType().getAsString()) + .concat(llvm::Twine(' ')) + .concat(FD->getNameAsString()) + .concat(" : ") + .str()); + Value *TmpRes = CGF.Builder.CreateCall(Func, {GString}); + Res = CGF.Builder.CreateAdd(Res, TmpRes); + + QualType CanonicalType = + FD->getType().getUnqualifiedType().getCanonicalType(); + + // We check whether we are in a recursive type + if (CanonicalType->isRecordType()) { + Value *TmpRes = + dumpRecord(CGF, CanonicalType, FieldPtr, Align, Func, Lvl + 1); + Res = CGF.Builder.CreateAdd(TmpRes, Res); + continue; + } + + // We try to determine the best format to print the current field + llvm::Twine Format = Types.find(CanonicalType) == Types.end() + ? Types[Context.VoidPtrTy] + : Types[CanonicalType]; + + Address FieldAddress = Address(FieldPtr, Align); + FieldPtr = CGF.Builder.CreateLoad(FieldAddress); + + // FIXME Need to handle bitfield here + GString = CGF.Builder.CreateGlobalStringPtr( + Format.concat(llvm::Twine('\n')).str()); + TmpRes = CGF.Builder.CreateCall(Func, {GString, FieldPtr}); + Res = CGF.Builder.CreateAdd(Res, TmpRes); + } + + GString = CGF.Builder.CreateGlobalStringPtr(Pad + "}\n"); + Value *TmpRes = CGF.Builder.CreateCall(Func, {GString}); + Res = CGF.Builder.CreateAdd(Res, TmpRes); + return Res; +} + RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, unsigned BuiltinID, const CallExpr *E, ReturnValueSlot ReturnValue) { @@ -1196,6 +1284,18 @@ return RValue::get(ComplexVal.first); } + case Builtin::BI__builtin_dump_struct: { + Value *Func = EmitScalarExpr(E->getArg(1)->IgnoreImpCasts()); + CharUnits Arg0Align = EmitPointerWithAlignment(E->getArg(0)).getAlignment(); + + const Expr *Arg0 = E->getArg(0)->IgnoreImpCasts(); + QualType Arg0Type = Arg0->getType()->getPointeeType(); + + Value *RecordPtr = EmitScalarExpr(Arg0); + Value *Res = dumpRecord(*this, Arg0Type, RecordPtr, Arg0Align, Func, 0); + return RValue::get(Res); + } + case Builtin::BI__builtin_cimag: case Builtin::BI__builtin_cimagf: case Builtin::BI__builtin_cimagl: Index: lib/Sema/SemaChecking.cpp =================================================================== --- lib/Sema/SemaChecking.cpp +++ lib/Sema/SemaChecking.cpp @@ -1110,6 +1110,62 @@ // so ensure that they are declared. DeclareGlobalNewDelete(); break; + case Builtin::BI__builtin_dump_struct: { + // We first want to ensure we are called with 2 arguments + if (checkArgCount(*this, TheCall, 2)) + return ExprError(); + // Ensure that the first argument is of type 'struct XX *' + const Expr *PtrArg = TheCall->getArg(0)->IgnoreParenImpCasts(); + const QualType PtrArgType = PtrArg->getType(); + if (!PtrArgType->isPointerType() || + !PtrArgType->getPointeeType()->isRecordType()) { + Diag(PtrArg->getLocStart(), diag::err_typecheck_convert_incompatible) + << PtrArgType << "structure pointer" << 1 << 0 << 3 << 1 << PtrArgType + << "structure pointer"; + return ExprError(); + } + + // Ensure that the second argument is of type 'FunctionType' + const Expr *FnPtrArg = TheCall->getArg(1)->IgnoreImpCasts(); + const QualType FnPtrArgType = FnPtrArg->getType(); + if (!FnPtrArgType->isPointerType()) { + Diag(FnPtrArg->getLocStart(), diag::err_typecheck_convert_incompatible) + << FnPtrArgType << "\'int (*)(const char *, ...)\'" << 1 << 0 << 3 + << 2 << FnPtrArgType << "\'int (*)(const char *, ...)\'"; + return ExprError(); + } + + const auto *FuncType = + FnPtrArgType->getPointeeType()->getAs(); + + if (!FuncType) { + Diag(FnPtrArg->getLocStart(), diag::err_typecheck_convert_incompatible) + << FnPtrArgType << "\'int (*)(const char *, ...)\'" << 1 << 0 << 3 + << 2 << FnPtrArgType << "\'int (*)(const char *, ...)\'"; + return ExprError(); + } + + if (const auto *FT = dyn_cast(FuncType)) { + if (!FT->getNumParams()) { + Diag(FnPtrArg->getLocStart(), diag::err_typecheck_convert_incompatible) + << FnPtrArgType << "\'int (*)(const char *, ...)\'" << 1 << 0 << 3 + << 2 << FnPtrArgType << "\'int (*)(const char *, ...)\'"; + return ExprError(); + } + QualType PT = FT->getParamType(0); + if (!FT->isVariadic() || FT->getReturnType() != Context.IntTy || + !PT->isPointerType() || !PT->getPointeeType()->isCharType() || + !PT->getPointeeType().isConstQualified()) { + Diag(FnPtrArg->getLocStart(), diag::err_typecheck_convert_incompatible) + << FnPtrArgType << "\'int (*)(const char *, ...)\'" << 1 << 0 << 3 + << 2 << FnPtrArgType << "\'int (*)(const char *, ...)\'"; + return ExprError(); + } + } + + TheCall->setType(Context.IntTy); + break; + } // check secure string manipulation functions where overflows // are detectable at compile time Index: test/CodeGen/dump-struct-builtin.c =================================================================== --- /dev/null +++ test/CodeGen/dump-struct-builtin.c @@ -0,0 +1,387 @@ +// RUN: %clang_cc1 -emit-llvm %s -o - | FileCheck %s + +#include "Inputs/stdio.h" + +void unit1() { + struct U1A { + short a; + }; + + struct U1A a = { + .a = 12, + }; + + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U1A, %struct.U1A* %a, i32 0, i32 0 + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[LOAD1:%[0-9]+]] = load i16, i16* [[RES1]], + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i16 [[LOAD1]]) + // CHECK: call i32 (i8*, ...) @printf( + __builtin_dump_struct(&a, &printf); +} + +void unit2() { + struct U2A { + unsigned short a; + }; + + struct U2A a = { + .a = 12, + }; + + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U2A, %struct.U2A* %a, i32 0, i32 0 + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[LOAD1:%[0-9]+]] = load i16, i16* [[RES1]], + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i16 [[LOAD1]]) + // CHECK: call i32 (i8*, ...) @printf( + __builtin_dump_struct(&a, &printf); +} + +void unit3() { + struct U3A { + int a; + }; + + struct U3A a = { + .a = 12, + }; + + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U3A, %struct.U3A* %a, i32 0, i32 0 + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[LOAD1:%[0-9]+]] = load i32, i32* [[RES1]], + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i32 [[LOAD1]]) + // CHECK: call i32 (i8*, ...) @printf( + __builtin_dump_struct(&a, &printf); +} + +void unit4() { + struct U4A { + unsigned int a; + }; + + struct U4A a = { + .a = 12, + }; + + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U4A, %struct.U4A* %a, i32 0, i32 0 + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[LOAD1:%[0-9]+]] = load i32, i32* [[RES1]], + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i32 [[LOAD1]]) + // CHECK: call i32 (i8*, ...) @printf( + __builtin_dump_struct(&a, &printf); +} + +void unit5() { + struct U5A { + long a; + }; + + struct U5A a = { + .a = 12, + }; + + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U5A, %struct.U5A* %a, i32 0, i32 0 + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[LOAD1:%[0-9]+]] = load i64, i64* [[RES1]], + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i64 [[LOAD1]]) + // CHECK: call i32 (i8*, ...) @printf( + __builtin_dump_struct(&a, &printf); +} + +void unit6() { + struct U6A { + unsigned long a; + }; + + struct U6A a = { + .a = 12, + }; + + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U6A, %struct.U6A* %a, i32 0, i32 0 + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[LOAD1:%[0-9]+]] = load i64, i64* [[RES1]], + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i64 [[LOAD1]]) + // CHECK: call i32 (i8*, ...) @printf( + __builtin_dump_struct(&a, &printf); +} + +void unit7() { + struct U7A { + long long a; + }; + + struct U7A a = { + .a = 12, + }; + + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U7A, %struct.U7A* %a, i32 0, i32 0 + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[LOAD1:%[0-9]+]] = load i64, i64* [[RES1]], + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i64 [[LOAD1]]) + // CHECK: call i32 (i8*, ...) @printf( + __builtin_dump_struct(&a, &printf); +} + +void unit8() { + struct U8A { + long long a; + }; + + struct U8A a = { + .a = 12, + }; + + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U8A, %struct.U8A* %a, i32 0, i32 0 + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[LOAD1:%[0-9]+]] = load i64, i64* [[RES1]], + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i64 [[LOAD1]]) + // CHECK: call i32 (i8*, ...) @printf( + __builtin_dump_struct(&a, &printf); +} + +void unit9() { + struct U9A { + char a; + }; + + struct U9A a = { + .a = 'a', + }; + + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U9A, %struct.U9A* %a, i32 0, i32 0 + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[LOAD1:%[0-9]+]] = load i8, i8* [[RES1]], + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i8 [[LOAD1]]) + // CHECK: call i32 (i8*, ...) @printf( + __builtin_dump_struct(&a, &printf); +} + +void unit10() { + struct U10A { + char *a; + }; + + struct U10A a = { + .a = "LSE", + }; + + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U10A, %struct.U10A* %a, i32 0, i32 0 + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[LOAD1:%[0-9]+]] = load i8*, i8** [[RES1]], + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i8* [[LOAD1]]) + // CHECK: call i32 (i8*, ...) @printf( + __builtin_dump_struct(&a, &printf); +} + +void unit11() { + struct U11A { + void *a; + }; + + struct U11A a = { + .a = (void *)0x12345678, + }; + + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U11A, %struct.U11A* %a, i32 0, i32 0 + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[LOAD1:%[0-9]+]] = load i8*, i8** [[RES1]], + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i8* [[LOAD1]]) + // CHECK: call i32 (i8*, ...) @printf( + __builtin_dump_struct(&a, &printf); +} + +void unit12() { + struct U12A { + const char *a; + }; + + struct U12A a = { + .a = "LSE", + }; + + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U12A, %struct.U12A* %a, i32 0, i32 0 + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[LOAD1:%[0-9]+]] = load i8*, i8** [[RES1]], + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i8* [[LOAD1]]) + // CHECK: call i32 (i8*, ...) @printf( + __builtin_dump_struct(&a, &printf); +} + +void unit13() { + typedef char *charstar; + struct U13A { + const charstar a; + }; + + struct U13A a = { + .a = "LSE", + }; + + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U13A, %struct.U13A* %a, i32 0, i32 0 + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[LOAD1:%[0-9]+]] = load i8*, i8** [[RES1]], + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i8* [[LOAD1]]) + // CHECK: call i32 (i8*, ...) @printf( + __builtin_dump_struct(&a, &printf); +} + +void unit14() { + struct U14A { + double a; + }; + + struct U14A a = { + .a = 1.123456, + }; + + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U14A, %struct.U14A* %a, i32 0, i32 0 + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[LOAD1:%[0-9]+]] = load double, double* [[RES1]], + // CHECK: call i32 (i8*, ...) @printf({{.*}}, double [[LOAD1]]) + // CHECK: call i32 (i8*, ...) @printf( + __builtin_dump_struct(&a, &printf); +} + +void unit15() { + struct U15A { + int a[3]; + }; + + struct U15A a = { + .a = {1, 2, 3}, + }; + + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.U15A, %struct.U15A* %a, i32 0, i32 0 + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[LOAD1:%[0-9]+]] = load [3 x i32], [3 x i32]* [[RES1]], + // CHECK: call i32 (i8*, ...) @printf({{.*}}, [3 x i32] [[LOAD1]]) + // CHECK: call i32 (i8*, ...) @printf( + __builtin_dump_struct(&a, &printf); +} + +void test1() { + struct T1A { + int a; + char *b; + }; + + struct T1A a = { + .a = 12, + .b = "LSE", + }; + + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.T1A, %struct.T1A* %a, i32 0, i32 0 + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[LOAD1:%[0-9]+]] = load i32, i32* [[RES1]], + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i32 [[LOAD1]]) + // CHECK: [[RES2:%[0-9]+]] = getelementptr inbounds %struct.T1A, %struct.T1A* %a, i32 0, i32 1 + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[LOAD2:%[0-9]+]] = load i8*, i8** [[RES2]], + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i8* [[LOAD2]]) + // CHECK: call i32 (i8*, ...) @printf( + __builtin_dump_struct(&a, &printf); +} + +void test2() { + struct T2A { + int a; + }; + + struct T2B { + int b; + struct T2A a; + }; + + struct T2B b = { + .b = 24, + .a = { + .a = 12, + } + }; + + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.T2B, %struct.T2B* %b, i32 0, i32 0 + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[LOAD1:%[0-9]+]] = load i32, i32* [[RES1]], + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i32 [[LOAD1]]) + // CHECK: [[NESTED_STRUCT:%[0-9]+]] = getelementptr inbounds %struct.T2B, %struct.T2B* %b, i32 0, i32 1 + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[RES2:%[0-9]+]] = getelementptr inbounds %struct.T2A, %struct.T2A* [[NESTED_STRUCT]], i32 0, i32 0 + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[LOAD2:%[0-9]+]] = load i32, i32* [[RES2]], + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i32 [[LOAD2]]) + // CHECK: call i32 (i8*, ...) @printf( + __builtin_dump_struct(&b, &printf); +} + +void test3() { + struct T3A { + union { + int a; + char b[4]; + }; + }; + + struct T3A a = { + .a = 42, + }; + + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.T3A, %struct.T3A* %a, i32 0, i32 0 + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[BC1:%[0-9]+]] = bitcast %union.anon* [[RES1]] to i32* + // CHECK: [[LOAD1:%[0-9]+]] = load i32, i32* [[BC1]], + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i32 [[LOAD1]]) + // CHECK: [[BC2:%[0-9]+]] = bitcast %union.anon* [[RES1]] to [4 x i8]* + // CHECK: [[LOAD2:%[0-9]+]] = load [4 x i8], [4 x i8]* [[BC2]], + // CHECK: call i32 (i8*, ...) @printf({{.*}}, [4 x i8] [[LOAD2]]) + // CHECK: call i32 (i8*, ...) @printf( + __builtin_dump_struct(&a, &printf); +} + +void test4() { + struct T4A { + union { + struct { + void *a; + }; + struct { + unsigned long b; + }; + }; + }; + + struct T4A a = { + .a = (void *)0x12345678, + }; + + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[RES1:%[0-9]+]] = getelementptr inbounds %struct.T4A, %struct.T4A* %a, i32 0, i32 0 + // CHECK: call i32 (i8*, ...) @printf( + // CHECK: [[BC1:%[0-9]+]] = bitcast %union.anon.0* [[RES1]] to %struct.anon* + // CHECK: [[RES2:%[0-9]+]] = getelementptr inbounds %struct.anon, %struct.anon* [[BC1]], i32 0, i32 0 + // CHECK: [[LOAD1:%[0-9]+]] = load i8*, i8** [[RES2]], + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i8* [[LOAD1]]) + + // CHECK: [[BC2:%[0-9]+]] = bitcast %union.anon.0* [[RES1]] to %struct.anon.1* + // CHECK: [[RES3:%[0-9]+]] = getelementptr inbounds %struct.anon.1, %struct.anon.1* [[BC2]], i32 0, i32 0 + // CHECK: [[LOAD2:%[0-9]+]] = load i64, i64* [[RES3]], + // CHECK: call i32 (i8*, ...) @printf({{.*}}, i64 [[LOAD2]]) + // CHECK: call i32 (i8*, ...) @printf( + __builtin_dump_struct(&a, &printf); +} Index: test/Sema/builtin-dump-struct.c =================================================================== --- /dev/null +++ test/Sema/builtin-dump-struct.c @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -triple i386-unknown-unknown -fsyntax-only -fno-spell-checking -verify %s + +void invalid_uses() { + struct A { + }; + struct A a; + void *b; + int (*goodfunc)(const char *, ...); + int (*badfunc1)(const char *); + int (*badfunc2)(int, ...); + void (*badfunc3)(const char *, ...); + int (*badfunc4)(char *, ...); + int (*badfunc5)(void); + + __builtin_dump_struct(); // expected-error {{too few arguments to function call, expected 2, have 0}} + __builtin_dump_struct(1); // expected-error {{too few arguments to function call, expected 2, have 1}} + __builtin_dump_struct(1, 2); // expected-error {{passing 'int' to parameter of incompatible type structure pointer: type mismatch at 1st parameter ('int' vs structure pointer)}} + __builtin_dump_struct(&a, 2); // expected-error {{passing 'int' to parameter of incompatible type 'int (*)(const char *, ...)': type mismatch at 2nd parameter ('int' vs 'int (*)(const char *, ...)')}} + __builtin_dump_struct(b, goodfunc); // expected-error {{passing 'void *' to parameter of incompatible type structure pointer: type mismatch at 1st parameter ('void *' vs structure pointer)}} + __builtin_dump_struct(&a, badfunc1); // expected-error {{passing 'int (*)(const char *)' to parameter of incompatible type 'int (*)(const char *, ...)': type mismatch at 2nd parameter ('int (*)(const char *)' vs 'int (*)(const char *, ...)')}} + __builtin_dump_struct(&a, badfunc2); // expected-error {{passing 'int (*)(int, ...)' to parameter of incompatible type 'int (*)(const char *, ...)': type mismatch at 2nd parameter ('int (*)(int, ...)' vs 'int (*)(const char *, ...)')}} + __builtin_dump_struct(&a, badfunc3); // expected-error {{passing 'void (*)(const char *, ...)' to parameter of incompatible type 'int (*)(const char *, ...)': type mismatch at 2nd parameter ('void (*)(const char *, ...)' vs 'int (*)(const char *, ...)')}} + __builtin_dump_struct(&a, badfunc4); // expected-error {{passing 'int (*)(char *, ...)' to parameter of incompatible type 'int (*)(const char *, ...)': type mismatch at 2nd parameter ('int (*)(char *, ...)' vs 'int (*)(const char *, ...)')}} + __builtin_dump_struct(&a, badfunc5); // expected-error {{passing 'int (*)(void)' to parameter of incompatible type 'int (*)(const char *, ...)': type mismatch at 2nd parameter ('int (*)(void)' vs 'int (*)(const char *, ...)')}} + __builtin_dump_struct(a, goodfunc); // expected-error {{passing 'struct A' to parameter of incompatible type structure pointer: type mismatch at 1st parameter ('struct A' vs structure pointer)}} +} + +void valid_uses() { + struct A { + }; + union B { + }; + + int (*goodfunc)(const char *, ...); + int (*goodfunc2)(); + struct A a; + union B b; + + __builtin_dump_struct(&a, goodfunc); + __builtin_dump_struct(&b, goodfunc); + __builtin_dump_struct(&a, goodfunc2); +}