From 92e986e0adb79e8a47f738bd608e6c97c547641d Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Thu, 22 Apr 2010 16:44:27 +0000 Subject: [PATCH] Implement template instantiation for Objective-C++ message sends. We support dependent receivers for class and instance messages, along with dependent message arguments (of course), and check as much as we can at template definition time. This commit also deals with a subtle aspect of template instantiation in Objective-C++, where the type 'T *' can morph from a dependent PointerType into a non-dependent ObjCObjectPointer type. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@102071 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/Expr.cpp | 6 +- lib/Sema/SemaExpr.cpp | 5 + lib/Sema/SemaExprObjC.cpp | 41 ++++++- lib/Sema/SemaType.cpp | 2 + lib/Sema/TreeTransform.h | 143 ++++++++++++++++++++++--- test/SemaObjCXX/instantiate-message.mm | 50 +++++++++ 6 files changed, 224 insertions(+), 23 deletions(-) create mode 100644 test/SemaObjCXX/instantiate-message.mm diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index e635b65841..036d5ef680 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -2248,7 +2248,7 @@ ObjCMessageExpr::ObjCMessageExpr(QualType T, Expr **Args, unsigned NumArgs, SourceLocation RBracLoc) : Expr(ObjCMessageExprClass, T, /*TypeDependent=*/false, - hasAnyValueDependentArguments(Args, NumArgs)), + /*ValueDependent=*/false), NumArgs(NumArgs), Kind(IsInstanceSuper? SuperInstance : SuperClass), HasMethod(Method != 0), SuperLoc(SuperLoc), SelectorOrMethod(reinterpret_cast(Method? Method @@ -2287,8 +2287,8 @@ ObjCMessageExpr::ObjCMessageExpr(QualType T, ObjCMethodDecl *Method, Expr **Args, unsigned NumArgs, SourceLocation RBracLoc) - : Expr(ObjCMessageExprClass, T, T->isDependentType(), - (T->isDependentType() || + : Expr(ObjCMessageExprClass, T, Receiver->isTypeDependent(), + (Receiver->isTypeDependent() || hasAnyValueDependentArguments(Args, NumArgs))), NumArgs(NumArgs), Kind(Instance), HasMethod(Method != 0), SelectorOrMethod(reinterpret_cast(Method? Method diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 3621f70ffc..7b09ddd75b 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -80,6 +80,9 @@ void Sema::DiagnoseSentinelCalls(NamedDecl *D, SourceLocation Loc, const SentinelAttr *attr = D->getAttr(); if (!attr) return; + + // FIXME: In C++0x, if any of the arguments are parameter pack + // expansions, we can't check for the sentinel now. int sentinelPos = attr->getSentinel(); int nullPos = attr->getNullPos(); @@ -155,6 +158,8 @@ void Sema::DiagnoseSentinelCalls(NamedDecl *D, SourceLocation Loc, } Expr *sentinelExpr = Args[sentinel]; if (sentinelExpr && (!isa(sentinelExpr) && + !sentinelExpr->isTypeDependent() && + !sentinelExpr->isValueDependent() && (!sentinelExpr->getType()->isPointerType() || !sentinelExpr->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull)))) { diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp index e6884bac33..71833f254c 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/lib/Sema/SemaExprObjC.cpp @@ -177,8 +177,12 @@ bool Sema::CheckMessageArgumentTypes(Expr **Args, unsigned NumArgs, QualType &ReturnType) { if (!Method) { // Apply default argument promotion as for (C99 6.5.2.2p6). - for (unsigned i = 0; i != NumArgs; i++) + for (unsigned i = 0; i != NumArgs; i++) { + if (Args[i]->isTypeDependent()) + continue; + DefaultArgumentPromotion(Args[i]); + } unsigned DiagID = isClassMessage ? diag::warn_class_method_not_found : diag::warn_inst_method_not_found; @@ -204,7 +208,12 @@ bool Sema::CheckMessageArgumentTypes(Expr **Args, unsigned NumArgs, bool IsError = false; for (unsigned i = 0; i < NumNamedArgs; i++) { + // We can't do any type-checking on a type-dependent argument. + if (Args[i]->isTypeDependent()) + continue; + Expr *argExpr = Args[i]; + ParmVarDecl *Param = Method->param_begin()[i]; assert(argExpr && "CheckMessageArgumentTypes(): missing expression"); @@ -226,8 +235,12 @@ bool Sema::CheckMessageArgumentTypes(Expr **Args, unsigned NumArgs, // Promote additional arguments to variadic methods. if (Method->isVariadic()) { - for (unsigned i = NumNamedArgs; i < NumArgs; ++i) + for (unsigned i = NumNamedArgs; i < NumArgs; ++i) { + if (Args[i]->isTypeDependent()) + continue; + IsError |= DefaultVariadicArgumentPromotion(Args[i], VariadicMethod); + } } else { // Check for extra arguments to non-variadic methods. if (NumArgs != NumNamedArgs) { @@ -677,8 +690,16 @@ Sema::OwningExprResult Sema::BuildClassMessage(TypeSourceInfo *ReceiverTypeInfo, SourceLocation SelectorLoc, SourceLocation RBracLoc, MultiExprArg ArgsIn) { - assert(!ReceiverType->isDependentType() && - "Dependent class messages not yet implemented"); + if (ReceiverType->isDependentType()) { + // If the receiver type is dependent, we can't type-check anything + // at this point. Build a dependent expression. + unsigned NumArgs = ArgsIn.size(); + Expr **Args = reinterpret_cast(ArgsIn.release()); + assert(SuperLoc.isInvalid() && "Message to super with dependent type"); + return Owned(ObjCMessageExpr::Create(Context, ReceiverType, LBracLoc, + ReceiverTypeInfo, Sel, /*Method=*/0, + Args, NumArgs, RBracLoc)); + } SourceLocation Loc = SuperLoc.isValid()? SuperLoc : ReceiverTypeInfo->getTypeLoc().getSourceRange().getBegin(); @@ -802,6 +823,18 @@ Sema::OwningExprResult Sema::BuildInstanceMessage(ExprArg ReceiverE, // and determine receiver type. Expr *Receiver = ReceiverE.takeAs(); if (Receiver) { + if (Receiver->isTypeDependent()) { + // If the receiver is type-dependent, we can't type-check anything + // at this point. Build a dependent expression. + unsigned NumArgs = ArgsIn.size(); + Expr **Args = reinterpret_cast(ArgsIn.release()); + assert(SuperLoc.isInvalid() && "Message to super with dependent type"); + return Owned(ObjCMessageExpr::Create(Context, Context.DependentTy, + LBracLoc, Receiver, Sel, + /*Method=*/0, Args, NumArgs, + RBracLoc)); + } + // If necessary, apply function/array conversion to the receiver. // C99 6.7.5.3p[7,8]. DefaultFunctionArrayLvalueConversion(Receiver); diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index ab1fc1824f..4f1bea19cc 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -499,6 +499,8 @@ QualType Sema::BuildPointerType(QualType T, unsigned Quals, Qs.removeRestrict(); } + assert(!T->isObjCInterfaceType() && "Should build ObjCObjectPointerType"); + // Build the pointer type. return Context.getQualifiedType(Context.getPointerType(T), Qs); } diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index ca9198490e..02f7417500 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -379,10 +379,6 @@ public: QualType RebuildMemberPointerType(QualType PointeeType, QualType ClassType, SourceLocation Sigil); - /// \brief Build a new Objective C object pointer type. - QualType RebuildObjCObjectPointerType(QualType PointeeType, - SourceLocation Sigil); - /// \brief Build a new array type given the element type, size /// modifier, size of the array (if known), size expression, and index type /// qualifiers. @@ -1695,6 +1691,43 @@ public: RParenLoc)); } + /// \brief Build a new Objective-C class message. + OwningExprResult RebuildObjCMessageExpr(TypeSourceInfo *ReceiverTypeInfo, + Selector Sel, + ObjCMethodDecl *Method, + SourceLocation LBracLoc, + MultiExprArg Args, + SourceLocation RBracLoc) { + // FIXME: Drops Method + return SemaRef.BuildClassMessage(ReceiverTypeInfo, + ReceiverTypeInfo->getType(), + /*SuperLoc=*/SourceLocation(), + Sel, + LBracLoc, + /*FIXME:*/LBracLoc, + RBracLoc, + move(Args)); + } + + /// \brief Build a new Objective-C instance message. + OwningExprResult RebuildObjCMessageExpr(ExprArg Receiver, + Selector Sel, + ObjCMethodDecl *Method, + SourceLocation LBracLoc, + MultiExprArg Args, + SourceLocation RBracLoc) { + // FIXME: Drops Method + QualType ReceiverType = static_cast(Receiver.get())->getType(); + return SemaRef.BuildInstanceMessage(move(Receiver), + ReceiverType, + /*SuperLoc=*/SourceLocation(), + Sel, + LBracLoc, + /*FIXME:*/LBracLoc, + RBracLoc, + move(Args)); + } + /// \brief Build a new Objective-C protocol expression. /// /// By default, performs semantic analysis to build the new expression. @@ -2272,7 +2305,42 @@ template QualType TreeTransform::TransformPointerType(TypeLocBuilder &TLB, PointerTypeLoc TL, QualType ObjectType) { - TransformPointerLikeType(PointerType); + QualType PointeeType + = getDerived().TransformType(TLB, TL.getPointeeLoc()); + if (PointeeType.isNull()) + return QualType(); + + QualType Result = TL.getType(); + if (PointeeType->isObjCInterfaceType()) { + // A dependent pointer type 'T *' has is being transformed such + // that an Objective-C class type is being replaced for 'T'. The + // resulting pointer type is an ObjCObjectPointerType, not a + // PointerType. + const ObjCInterfaceType *IFace = PointeeType->getAs(); + Result = SemaRef.Context.getObjCObjectPointerType(PointeeType, + const_cast( + IFace->qual_begin()), + IFace->getNumProtocols()); + + ObjCObjectPointerTypeLoc NewT = TLB.push(Result); + NewT.setStarLoc(TL.getSigilLoc()); + NewT.setHasProtocolsAsWritten(false); + NewT.setLAngleLoc(SourceLocation()); + NewT.setRAngleLoc(SourceLocation()); + NewT.setHasBaseTypeAsWritten(true); + return Result; + } + + if (getDerived().AlwaysRebuild() || + PointeeType != TL.getPointeeLoc().getType()) { + Result = getDerived().RebuildPointerType(PointeeType, TL.getSigilLoc()); + if (Result.isNull()) + return QualType(); + } + + PointerTypeLoc NewT = TLB.push(Result); + NewT.setSigilLoc(TL.getSigilLoc()); + return Result; } template @@ -5482,9 +5550,59 @@ TreeTransform::TransformObjCEncodeExpr(ObjCEncodeExpr *E) { template Sema::OwningExprResult TreeTransform::TransformObjCMessageExpr(ObjCMessageExpr *E) { - // FIXME: Implement this! - assert(false && "Cannot transform Objective-C expressions yet"); - return SemaRef.Owned(E->Retain()); + // Transform arguments. + bool ArgChanged = false; + ASTOwningVector<&ActionBase::DeleteExpr> Args(SemaRef); + for (unsigned I = 0, N = E->getNumArgs(); I != N; ++I) { + OwningExprResult Arg = getDerived().TransformExpr(E->getArg(I)); + if (Arg.isInvalid()) + return SemaRef.ExprError(); + + ArgChanged = ArgChanged || Arg.get() != E->getArg(I); + Args.push_back(Arg.takeAs()); + } + + if (E->getReceiverKind() == ObjCMessageExpr::Class) { + // Class message: transform the receiver type. + TypeSourceInfo *ReceiverTypeInfo + = getDerived().TransformType(E->getClassReceiverTypeInfo()); + if (!ReceiverTypeInfo) + return SemaRef.ExprError(); + + // If nothing changed, just retain the existing message send. + if (!getDerived().AlwaysRebuild() && + ReceiverTypeInfo == E->getClassReceiverTypeInfo() && !ArgChanged) + return SemaRef.Owned(E->Retain()); + + // Build a new class message send. + return getDerived().RebuildObjCMessageExpr(ReceiverTypeInfo, + E->getSelector(), + E->getMethodDecl(), + E->getLeftLoc(), + move_arg(Args), + E->getRightLoc()); + } + + // Instance message: transform the receiver + assert(E->getReceiverKind() == ObjCMessageExpr::Instance && + "Only class and instance messages may be instantiated"); + OwningExprResult Receiver + = getDerived().TransformExpr(E->getInstanceReceiver()); + if (Receiver.isInvalid()) + return SemaRef.ExprError(); + + // If nothing changed, just retain the existing message send. + if (!getDerived().AlwaysRebuild() && + Receiver.get() == E->getInstanceReceiver() && !ArgChanged) + return SemaRef.Owned(E->Retain()); + + // Build a new instance message send. + return getDerived().RebuildObjCMessageExpr(move(Receiver), + E->getSelector(), + E->getMethodDecl(), + E->getLeftLoc(), + move_arg(Args), + E->getRightLoc()); } template @@ -5631,14 +5749,6 @@ TreeTransform::RebuildMemberPointerType(QualType PointeeType, Sigil, getDerived().getBaseEntity()); } -template -QualType -TreeTransform::RebuildObjCObjectPointerType(QualType PointeeType, - SourceLocation Sigil) { - return SemaRef.BuildPointerType(PointeeType, Qualifiers(), Sigil, - getDerived().getBaseEntity()); -} - template QualType TreeTransform::RebuildArrayType(QualType ElementType, @@ -5768,6 +5878,7 @@ QualType TreeTransform::RebuildUnresolvedUsingType(Decl *D) { assert(D && "no decl found"); if (D->isInvalidDecl()) return QualType(); + // FIXME: Doesn't account for ObjCInterfaceDecl! TypeDecl *Ty; if (isa(D)) { UsingDecl *Using = cast(D); diff --git a/test/SemaObjCXX/instantiate-message.mm b/test/SemaObjCXX/instantiate-message.mm new file mode 100644 index 0000000000..46c8ede26a --- /dev/null +++ b/test/SemaObjCXX/instantiate-message.mm @@ -0,0 +1,50 @@ +// RUN: %clang -cc1 -fsyntax-only -verify %s + +// Test template instantiation of Objective-C message sends. + +@interface ClassMethods ++ (ClassMethods *)method1:(void*)ptr; +@end + +template +struct identity { + typedef T type; +}; + +template +void test_class_method(Arg1 arg1) { + R *result1 = [T method1:arg1]; + R *result2 = [typename identity::type method1:arg1]; + R *result3 = [ClassMethods method1:arg1]; // expected-error{{cannot initialize a variable of type 'ClassMethods2 *' with an rvalue of type 'ClassMethods *'}} +} + +template void test_class_method(void*); +template void test_class_method(int*); + +@interface ClassMethods2 ++ (ClassMethods2 *)method1:(int*)ptr; +@end + +template void test_class_method(int*); // expected-note{{in instantiation of}} + + +@interface InstanceMethods +- (InstanceMethods *)method1:(void*)ptr; +@end + +template +void test_instance_method(Arg1 arg1) { + T *receiver = 0; + InstanceMethods *im = 0; + R *result1 = [receiver method1:arg1]; + R *result2 = [im method1:arg1]; // expected-error{{cannot initialize a variable of type 'InstanceMethods2 *' with an rvalue of type 'InstanceMethods *'}} +} + +template void test_instance_method(void*); +template void test_instance_method(int*); + +@interface InstanceMethods2 +- (InstanceMethods2 *)method1:(void*)ptr; +@end + +template void test_instance_method(int*); // expected-note{{in instantiation of}} -- 2.40.0