Change the representation of builtin functions in the AST

(__builtin_* etc.) so that it isn't possible to take their address.
Specifically, introduce a new type to represent a reference to a builtin
function, and a new cast kind to convert it to a function pointer in the
operand of a call.  Fixes PR13195.

llvm-svn: 162962
This commit is contained in:
Eli Friedman 2012-08-31 00:14:07 +00:00
parent 98cfa1044f
commit 34866c7719
27 changed files with 110 additions and 44 deletions

View File

@ -676,6 +676,7 @@ public:
CanQualType FloatComplexTy, DoubleComplexTy, LongDoubleComplexTy; CanQualType FloatComplexTy, DoubleComplexTy, LongDoubleComplexTy;
CanQualType VoidPtrTy, NullPtrTy; CanQualType VoidPtrTy, NullPtrTy;
CanQualType DependentTy, OverloadTy, BoundMemberTy, UnknownAnyTy; CanQualType DependentTy, OverloadTy, BoundMemberTy, UnknownAnyTy;
CanQualType BuiltinFnTy;
CanQualType PseudoObjectTy, ARCUnbridgedCastTy; CanQualType PseudoObjectTy, ARCUnbridgedCastTy;
CanQualType ObjCBuiltinIdTy, ObjCBuiltinClassTy, ObjCBuiltinSelTy; CanQualType ObjCBuiltinIdTy, ObjCBuiltinClassTy, ObjCBuiltinSelTy;
CanQualType ObjCBuiltinBoolTy; CanQualType ObjCBuiltinBoolTy;

View File

@ -206,6 +206,8 @@ PLACEHOLDER_TYPE(PseudoObject, PseudoObjectTy)
// unknown type, most notably explicit casts. // unknown type, most notably explicit casts.
PLACEHOLDER_TYPE(UnknownAny, UnknownAnyTy) PLACEHOLDER_TYPE(UnknownAny, UnknownAnyTy)
PLACEHOLDER_TYPE(BuiltinFn, BuiltinFnTy)
// The type of a cast which, in ARC, would normally require a // The type of a cast which, in ARC, would normally require a
// __bridge, but which might be okay depending on the immediate // __bridge, but which might be okay depending on the immediate
// context. // context.

View File

@ -288,7 +288,11 @@ enum CastKind {
/// ///
/// This particular cast kind is used for the conversion from a C++11 /// This particular cast kind is used for the conversion from a C++11
/// lambda expression to a block pointer. /// lambda expression to a block pointer.
CK_CopyAndAutoreleaseBlockObject CK_CopyAndAutoreleaseBlockObject,
// Convert a builtin function to a function pointer; only allowed in the
// callee of a call expression.
CK_BuiltinFnToFnPtr
}; };
static const CastKind CK_Invalid = static_cast<CastKind>(-1); static const CastKind CK_Invalid = static_cast<CastKind>(-1);

View File

@ -4910,6 +4910,8 @@ def note_callee_decl : Note<
"%0 declared here">; "%0 declared here">;
def note_defined_here : Note<"%0 defined here">; def note_defined_here : Note<"%0 defined here">;
def err_builtin_fn_use : Error<"builtin functions must be directly called">;
def warn_call_wrong_number_of_arguments : Warning< def warn_call_wrong_number_of_arguments : Warning<
"too %select{few|many}0 arguments in call to %1">; "too %select{few|many}0 arguments in call to %1">;
def err_atomic_builtin_must_be_pointer : Error< def err_atomic_builtin_must_be_pointer : Error<

View File

@ -642,7 +642,9 @@ namespace clang {
/// \brief The pseudo-object placeholder type. /// \brief The pseudo-object placeholder type.
PREDEF_TYPE_PSEUDO_OBJECT = 35, PREDEF_TYPE_PSEUDO_OBJECT = 35,
/// \brief The __va_list_tag placeholder type. /// \brief The __va_list_tag placeholder type.
PREDEF_TYPE_VA_LIST_TAG = 36 PREDEF_TYPE_VA_LIST_TAG = 36,
/// \brief The placeholder type for builtin functions.
PREDEF_TYPE_BUILTIN_FN = 37
}; };
/// \brief The number of predefined type IDs that are reserved for /// \brief The number of predefined type IDs that are reserved for

View File

@ -803,6 +803,9 @@ void ASTContext::InitBuiltinTypes(const TargetInfo &Target) {
// Placeholder type for unbridged ARC casts. // Placeholder type for unbridged ARC casts.
InitBuiltinType(ARCUnbridgedCastTy, BuiltinType::ARCUnbridgedCast); InitBuiltinType(ARCUnbridgedCastTy, BuiltinType::ARCUnbridgedCast);
// Placeholder type for builtin functions.
InitBuiltinType(BuiltinFnTy, BuiltinType::BuiltinFn);
// C99 6.2.5p11. // C99 6.2.5p11.
FloatComplexTy = getComplexType(FloatTy); FloatComplexTy = getComplexType(FloatTy);
DoubleComplexTy = getComplexType(DoubleTy); DoubleComplexTy = getComplexType(DoubleTy);

View File

@ -1310,12 +1310,16 @@ void CastExpr::CheckCastConsistency() const {
assert(getType()->isBlockPointerType()); assert(getType()->isBlockPointerType());
assert(getSubExpr()->getType()->isBlockPointerType()); assert(getSubExpr()->getType()->isBlockPointerType());
goto CheckNoBasePath; goto CheckNoBasePath;
case CK_FunctionToPointerDecay:
assert(getType()->isPointerType());
assert(getSubExpr()->getType()->isFunctionType());
goto CheckNoBasePath;
// These should not have an inheritance path. // These should not have an inheritance path.
case CK_Dynamic: case CK_Dynamic:
case CK_ToUnion: case CK_ToUnion:
case CK_ArrayToPointerDecay: case CK_ArrayToPointerDecay:
case CK_FunctionToPointerDecay:
case CK_NullToMemberPointer: case CK_NullToMemberPointer:
case CK_NullToPointer: case CK_NullToPointer:
case CK_ConstructorConversion: case CK_ConstructorConversion:
@ -1356,6 +1360,7 @@ void CastExpr::CheckCastConsistency() const {
case CK_IntegralComplexToBoolean: case CK_IntegralComplexToBoolean:
case CK_LValueBitCast: // -> bool& case CK_LValueBitCast: // -> bool&
case CK_UserDefinedConversion: // operator bool() case CK_UserDefinedConversion: // operator bool()
case CK_BuiltinFnToFnPtr:
CheckNoBasePath: CheckNoBasePath:
assert(path_empty() && "Cast kind should not have a base path!"); assert(path_empty() && "Cast kind should not have a base path!");
break; break;
@ -1468,6 +1473,8 @@ const char *CastExpr::getCastKindName() const {
return "NonAtomicToAtomic"; return "NonAtomicToAtomic";
case CK_CopyAndAutoreleaseBlockObject: case CK_CopyAndAutoreleaseBlockObject:
return "CopyAndAutoreleaseBlockObject"; return "CopyAndAutoreleaseBlockObject";
case CK_BuiltinFnToFnPtr:
return "BuiltinFnToFnPtr";
} }
llvm_unreachable("Unhandled cast kind!"); llvm_unreachable("Unhandled cast kind!");

View File

@ -5357,6 +5357,7 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) {
case CK_IntegralRealToComplex: case CK_IntegralRealToComplex:
case CK_IntegralComplexCast: case CK_IntegralComplexCast:
case CK_IntegralComplexToFloatingComplex: case CK_IntegralComplexToFloatingComplex:
case CK_BuiltinFnToFnPtr:
llvm_unreachable("invalid cast kind for integral value"); llvm_unreachable("invalid cast kind for integral value");
case CK_BitCast: case CK_BitCast:
@ -5843,6 +5844,7 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) {
case CK_ARCReclaimReturnedObject: case CK_ARCReclaimReturnedObject:
case CK_ARCExtendBlockObject: case CK_ARCExtendBlockObject:
case CK_CopyAndAutoreleaseBlockObject: case CK_CopyAndAutoreleaseBlockObject:
case CK_BuiltinFnToFnPtr:
llvm_unreachable("invalid cast kind for complex value"); llvm_unreachable("invalid cast kind for complex value");
case CK_LValueToRValue: case CK_LValueToRValue:

View File

@ -344,6 +344,7 @@ NSAPI::getNSNumberFactoryMethodKind(QualType T) const {
case BuiltinType::ARCUnbridgedCast: case BuiltinType::ARCUnbridgedCast:
case BuiltinType::Half: case BuiltinType::Half:
case BuiltinType::PseudoObject: case BuiltinType::PseudoObject:
case BuiltinType::BuiltinFn:
break; break;
} }

View File

@ -1480,6 +1480,7 @@ StringRef BuiltinType::getName(const PrintingPolicy &Policy) const {
case Dependent: return "<dependent type>"; case Dependent: return "<dependent type>";
case UnknownAny: return "<unknown type>"; case UnknownAny: return "<unknown type>";
case ARCUnbridgedCast: return "<ARC unbridged cast type>"; case ARCUnbridgedCast: return "<ARC unbridged cast type>";
case BuiltinFn: return "<builtin fn type>";
case ObjCId: return "id"; case ObjCId: return "id";
case ObjCClass: return "Class"; case ObjCClass: return "Class";
case ObjCSel: return "SEL"; case ObjCSel: return "SEL";

View File

@ -241,6 +241,7 @@ TypeSpecifierType BuiltinTypeLoc::getWrittenTypeSpec() const {
case BuiltinType::ObjCId: case BuiltinType::ObjCId:
case BuiltinType::ObjCClass: case BuiltinType::ObjCClass:
case BuiltinType::ObjCSel: case BuiltinType::ObjCSel:
case BuiltinType::BuiltinFn:
return TST_unspecified; return TST_unspecified;
} }

View File

@ -2396,7 +2396,10 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) {
case CK_Dependent: case CK_Dependent:
llvm_unreachable("dependent cast kind in IR gen!"); llvm_unreachable("dependent cast kind in IR gen!");
case CK_BuiltinFnToFnPtr:
llvm_unreachable("builtin functions are handled elsewhere");
// These two casts are currently treated as no-ops, although they could // These two casts are currently treated as no-ops, although they could
// potentially be real operations depending on the target's ABI. // potentially be real operations depending on the target's ABI.
case CK_NonAtomicToAtomic: case CK_NonAtomicToAtomic:

View File

@ -647,6 +647,7 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) {
case CK_ARCReclaimReturnedObject: case CK_ARCReclaimReturnedObject:
case CK_ARCExtendBlockObject: case CK_ARCExtendBlockObject:
case CK_CopyAndAutoreleaseBlockObject: case CK_CopyAndAutoreleaseBlockObject:
case CK_BuiltinFnToFnPtr:
llvm_unreachable("cast kind invalid for aggregate types"); llvm_unreachable("cast kind invalid for aggregate types");
} }
} }

View File

@ -427,6 +427,7 @@ ComplexPairTy ComplexExprEmitter::EmitCast(CastExpr::CastKind CK, Expr *Op,
case CK_ARCReclaimReturnedObject: case CK_ARCReclaimReturnedObject:
case CK_ARCExtendBlockObject: case CK_ARCExtendBlockObject:
case CK_CopyAndAutoreleaseBlockObject: case CK_CopyAndAutoreleaseBlockObject:
case CK_BuiltinFnToFnPtr:
llvm_unreachable("invalid cast kind for complex value"); llvm_unreachable("invalid cast kind for complex value");
case CK_FloatingRealToComplex: case CK_FloatingRealToComplex:

View File

@ -691,6 +691,9 @@ public:
case CK_Dependent: llvm_unreachable("saw dependent cast!"); case CK_Dependent: llvm_unreachable("saw dependent cast!");
case CK_BuiltinFnToFnPtr:
llvm_unreachable("builtin functions are handled elsewhere");
case CK_ReinterpretMemberPointer: case CK_ReinterpretMemberPointer:
case CK_DerivedToBaseMemberPointer: case CK_DerivedToBaseMemberPointer:
case CK_BaseToDerivedMemberPointer: case CK_BaseToDerivedMemberPointer:

View File

@ -1036,7 +1036,9 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
// are in the same order as in the CastKind enum. // are in the same order as in the CastKind enum.
switch (Kind) { switch (Kind) {
case CK_Dependent: llvm_unreachable("dependent cast kind in IR gen!"); case CK_Dependent: llvm_unreachable("dependent cast kind in IR gen!");
case CK_BuiltinFnToFnPtr:
llvm_unreachable("builtin functions are handled elsewhere");
case CK_LValueBitCast: case CK_LValueBitCast:
case CK_ObjCObjectLValueCast: { case CK_ObjCObjectLValueCast: {
Value *V = EmitLValue(E).getAddress(); Value *V = EmitLValue(E).getAddress();

View File

@ -920,6 +920,7 @@ static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
case CK_ARCExtendBlockObject: case CK_ARCExtendBlockObject:
case CK_NonAtomicToAtomic: case CK_NonAtomicToAtomic:
case CK_CopyAndAutoreleaseBlockObject: case CK_CopyAndAutoreleaseBlockObject:
case CK_BuiltinFnToFnPtr:
return false; return false;
} }
} }

View File

@ -1232,14 +1232,14 @@ Sema::SemaBuiltinAtomicOverloaded(ExprResult TheCallResult) {
NewBuiltinDecl, NewBuiltinDecl,
/*enclosing*/ false, /*enclosing*/ false,
DRE->getLocation(), DRE->getLocation(),
NewBuiltinDecl->getType(), Context.BuiltinFnTy,
DRE->getValueKind()); DRE->getValueKind());
// Set the callee in the CallExpr. // Set the callee in the CallExpr.
// FIXME: This leaks the original parens and implicit casts. // FIXME: This loses syntactic information.
ExprResult PromotedCall = UsualUnaryConversions(NewDRE); QualType CalleePtrTy = Context.getPointerType(NewBuiltinDecl->getType());
if (PromotedCall.isInvalid()) ExprResult PromotedCall = ImpCastExprToType(NewDRE, CalleePtrTy,
return ExprError(); CK_BuiltinFnToFnPtr);
TheCall->setCallee(PromotedCall.take()); TheCall->setCallee(PromotedCall.take());
// Change the result type of the call to match the original value type. This // Change the result type of the call to match the original value type. This

View File

@ -7847,8 +7847,8 @@ void Sema::DefineImplicitCopyAssignment(SourceLocation CurrentLocation,
} }
CollectableMemCpyRef = BuildDeclRefExpr(CollectableMemCpy, CollectableMemCpyRef = BuildDeclRefExpr(CollectableMemCpy,
CollectableMemCpy->getType(), Context.BuiltinFnTy,
VK_LValue, Loc, 0).take(); VK_RValue, Loc, 0).take();
assert(CollectableMemCpyRef && "Builtin reference cannot fail"); assert(CollectableMemCpyRef && "Builtin reference cannot fail");
} }
} }
@ -7867,8 +7867,8 @@ void Sema::DefineImplicitCopyAssignment(SourceLocation CurrentLocation,
} }
BuiltinMemCpyRef = BuildDeclRefExpr(BuiltinMemCpy, BuiltinMemCpyRef = BuildDeclRefExpr(BuiltinMemCpy,
BuiltinMemCpy->getType(), Context.BuiltinFnTy,
VK_LValue, Loc, 0).take(); VK_RValue, Loc, 0).take();
assert(BuiltinMemCpyRef && "Builtin reference cannot fail"); assert(BuiltinMemCpyRef && "Builtin reference cannot fail");
} }
@ -8396,8 +8396,8 @@ void Sema::DefineImplicitMoveAssignment(SourceLocation CurrentLocation,
} }
CollectableMemCpyRef = BuildDeclRefExpr(CollectableMemCpy, CollectableMemCpyRef = BuildDeclRefExpr(CollectableMemCpy,
CollectableMemCpy->getType(), Context.BuiltinFnTy,
VK_LValue, Loc, 0).take(); VK_RValue, Loc, 0).take();
assert(CollectableMemCpyRef && "Builtin reference cannot fail"); assert(CollectableMemCpyRef && "Builtin reference cannot fail");
} }
} }
@ -8416,8 +8416,8 @@ void Sema::DefineImplicitMoveAssignment(SourceLocation CurrentLocation,
} }
BuiltinMemCpyRef = BuildDeclRefExpr(BuiltinMemCpy, BuiltinMemCpyRef = BuildDeclRefExpr(BuiltinMemCpy,
BuiltinMemCpy->getType(), Context.BuiltinFnTy,
VK_LValue, Loc, 0).take(); VK_RValue, Loc, 0).take();
assert(BuiltinMemCpyRef && "Builtin reference cannot fail"); assert(BuiltinMemCpyRef && "Builtin reference cannot fail");
} }

View File

@ -2416,6 +2416,14 @@ Sema::BuildDeclarationNameExpr(const CXXScopeSpec &SS,
} }
case Decl::Function: { case Decl::Function: {
if (unsigned BID = cast<FunctionDecl>(VD)->getBuiltinID()) {
if (!Context.BuiltinInfo.isPredefinedLibFunction(BID)) {
type = Context.BuiltinFnTy;
valueKind = VK_RValue;
break;
}
}
const FunctionType *fty = type->castAs<FunctionType>(); const FunctionType *fty = type->castAs<FunctionType>();
// If we're referring to a function with an __unknown_anytype // If we're referring to a function with an __unknown_anytype
@ -3929,9 +3937,19 @@ Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl,
SourceLocation RParenLoc, SourceLocation RParenLoc,
Expr *Config, bool IsExecConfig) { Expr *Config, bool IsExecConfig) {
FunctionDecl *FDecl = dyn_cast_or_null<FunctionDecl>(NDecl); FunctionDecl *FDecl = dyn_cast_or_null<FunctionDecl>(NDecl);
unsigned BuiltinID = (FDecl ? FDecl->getBuiltinID() : 0);
// Promote the function operand. // Promote the function operand.
ExprResult Result = UsualUnaryConversions(Fn); // We special-case function promotion here because we only allow promoting
// builtin functions to function pointers in the callee of a call.
ExprResult Result;
if (BuiltinID &&
Fn->getType()->isSpecificBuiltinType(BuiltinType::BuiltinFn)) {
Result = ImpCastExprToType(Fn, Context.getPointerType(FDecl->getType()),
CK_BuiltinFnToFnPtr).take();
} else {
Result = UsualUnaryConversions(Fn);
}
if (Result.isInvalid()) if (Result.isInvalid())
return ExprError(); return ExprError();
Fn = Result.take(); Fn = Result.take();
@ -3953,8 +3971,6 @@ Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl,
VK_RValue, VK_RValue,
RParenLoc); RParenLoc);
unsigned BuiltinID = (FDecl ? FDecl->getBuiltinID() : 0);
// Bail out early if calling a builtin with custom typechecking. // Bail out early if calling a builtin with custom typechecking.
if (BuiltinID && Context.BuiltinInfo.hasCustomTypechecking(BuiltinID)) if (BuiltinID && Context.BuiltinInfo.hasCustomTypechecking(BuiltinID))
return CheckBuiltinFunctionCall(BuiltinID, TheCall); return CheckBuiltinFunctionCall(BuiltinID, TheCall);
@ -8635,6 +8651,7 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc,
break; break;
case UO_Deref: { case UO_Deref: {
Input = DefaultFunctionArrayLvalueConversion(Input.take()); Input = DefaultFunctionArrayLvalueConversion(Input.take());
if (Input.isInvalid()) return ExprError();
resultType = CheckIndirectionOperand(*this, Input.get(), VK, OpLoc); resultType = CheckIndirectionOperand(*this, Input.get(), VK, OpLoc);
break; break;
} }
@ -11747,6 +11764,10 @@ ExprResult Sema::CheckPlaceholderExpr(Expr *E) {
case BuiltinType::PseudoObject: case BuiltinType::PseudoObject:
return checkPseudoObjectRValue(E); return checkPseudoObjectRValue(E);
case BuiltinType::BuiltinFn:
Diag(E->getLocStart(), diag::err_builtin_fn_use);
return ExprError();
// Everything else should be impossible. // Everything else should be impossible.
#define BUILTIN_TYPE(Id, SingletonId) \ #define BUILTIN_TYPE(Id, SingletonId) \
case BuiltinType::Id: case BuiltinType::Id:

View File

@ -2413,17 +2413,16 @@ public:
// Build a reference to the __builtin_shufflevector builtin // Build a reference to the __builtin_shufflevector builtin
FunctionDecl *Builtin = cast<FunctionDecl>(*Lookup.first); FunctionDecl *Builtin = cast<FunctionDecl>(*Lookup.first);
ExprResult Callee Expr *Callee = new (SemaRef.Context) DeclRefExpr(Builtin, false,
= SemaRef.Owned(new (SemaRef.Context) DeclRefExpr(Builtin, false, SemaRef.Context.BuiltinFnTy,
Builtin->getType(), VK_RValue, BuiltinLoc);
VK_LValue, BuiltinLoc)); QualType CalleePtrTy = SemaRef.Context.getPointerType(Builtin->getType());
Callee = SemaRef.UsualUnaryConversions(Callee.take()); Callee = SemaRef.ImpCastExprToType(Callee, CalleePtrTy,
if (Callee.isInvalid()) CK_BuiltinFnToFnPtr).take();
return ExprError();
// Build the CallExpr // Build the CallExpr
ExprResult TheCall = SemaRef.Owned( ExprResult TheCall = SemaRef.Owned(
new (SemaRef.Context) CallExpr(SemaRef.Context, Callee.take(), SubExprs, new (SemaRef.Context) CallExpr(SemaRef.Context, Callee, SubExprs,
Builtin->getCallResultType(), Builtin->getCallResultType(),
Expr::getValueKindForType(Builtin->getResultType()), Expr::getValueKindForType(Builtin->getResultType()),
RParenLoc)); RParenLoc));

View File

@ -60,6 +60,9 @@ serialization::TypeIdxFromBuiltin(const BuiltinType *BT) {
case BuiltinType::ObjCId: ID = PREDEF_TYPE_OBJC_ID; break; case BuiltinType::ObjCId: ID = PREDEF_TYPE_OBJC_ID; break;
case BuiltinType::ObjCClass: ID = PREDEF_TYPE_OBJC_CLASS; break; case BuiltinType::ObjCClass: ID = PREDEF_TYPE_OBJC_CLASS; break;
case BuiltinType::ObjCSel: ID = PREDEF_TYPE_OBJC_SEL; break; case BuiltinType::ObjCSel: ID = PREDEF_TYPE_OBJC_SEL; break;
case BuiltinType::BuiltinFn:
ID = PREDEF_TYPE_BUILTIN_FN; break;
} }
return TypeIdx(ID); return TypeIdx(ID);

View File

@ -4466,6 +4466,10 @@ QualType ASTReader::GetType(TypeID ID) {
case PREDEF_TYPE_VA_LIST_TAG: case PREDEF_TYPE_VA_LIST_TAG:
T = Context.getVaListTagType(); T = Context.getVaListTagType();
break; break;
case PREDEF_TYPE_BUILTIN_FN:
T = Context.BuiltinFnTy;
break;
} }
assert(!T.isNull() && "Unknown predefined type"); assert(!T.isNull() && "Unknown predefined type");

View File

@ -266,7 +266,8 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
case CK_NoOp: case CK_NoOp:
case CK_ConstructorConversion: case CK_ConstructorConversion:
case CK_UserDefinedConversion: case CK_UserDefinedConversion:
case CK_FunctionToPointerDecay: { case CK_FunctionToPointerDecay:
case CK_BuiltinFnToFnPtr: {
// Copy the SVal of Ex to CastE. // Copy the SVal of Ex to CastE.
ProgramStateRef state = Pred->getState(); ProgramStateRef state = Pred->getState();
const LocationContext *LCtx = Pred->getLocationContext(); const LocationContext *LCtx = Pred->getLocationContext();

View File

@ -7,15 +7,3 @@ int main() {
// CHECK: call signext i8 @memmove() // CHECK: call signext i8 @memmove()
return memmove(); return memmove();
} }
// <rdar://problem/10063539>
template<int (*Compare)(const char *s1, const char *s2)>
int equal(const char *s1, const char *s2) {
return Compare(s1, s2) == 0;
}
// CHECK: define weak_odr i32 @_Z5equalIXadL_Z16__builtin_strcmpPKcS1_EEEiS1_S1_
// CHECK: call i32 @strcmp
template int equal<&__builtin_strcmp>(const char*, const char*);

View File

@ -10,7 +10,7 @@ int main() {
static int ary[__builtin_classify_type(a)]; static int ary[__builtin_classify_type(a)];
static int ary2[(__builtin_classify_type)(a)]; // expected-error{{variable length array declaration can not have 'static' storage duration}} static int ary2[(__builtin_classify_type)(a)]; // expected-error{{variable length array declaration can not have 'static' storage duration}}
static int ary3[(*__builtin_classify_type)(a)]; // expected-error{{variable length array declaration can not have 'static' storage duration}} static int ary3[(*__builtin_classify_type)(a)]; // expected-error{{builtin functions must be directly called}}
int result; int result;

View File

@ -7,3 +7,16 @@ void f() {
} }
void a() { __builtin_va_list x, y; ::__builtin_va_copy(x, y); } void a() { __builtin_va_list x, y; ::__builtin_va_copy(x, y); }
// <rdar://problem/10063539>
template<int (*Compare)(const char *s1, const char *s2)>
int equal(const char *s1, const char *s2) {
return Compare(s1, s2) == 0;
}
// FIXME: Our error recovery here sucks
template int equal<&__builtin_strcmp>(const char*, const char*); // expected-error {{builtin functions must be directly called}} expected-error {{expected unqualified-id}} expected-error {{expected ')'}} expected-note {{to match this '('}}
// PR13195
void f2() {
__builtin_isnan; // expected-error {{builtin functions must be directly called}}
}