From 410fc7ae89f4818e653ec34fcb1c3db25a281393 Mon Sep 17 00:00:00 2001 From: Eli Friedman Date: Fri, 30 Mar 2012 01:13:43 +0000 Subject: [PATCH] Make sure we perform the relevant implied conversions correctly for ObjC methods with related result types. PR12384. llvm-svn: 153716 --- clang/lib/Sema/SemaStmt.cpp | 37 ++++++++++--------- clang/test/CodeGenObjC/arc.m | 16 ++------ clang/test/SemaObjC/instancetype.m | 6 +-- .../SemaObjC/related-result-type-inference.m | 9 +++++ 4 files changed, 35 insertions(+), 33 deletions(-) diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 6db66a5eff8f..39e8a1a1b92d 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -1945,24 +1945,21 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { return ActOnCapScopeReturnStmt(ReturnLoc, RetValExp); QualType FnRetType; - QualType DeclaredRetType; + QualType RelatedRetType; if (const FunctionDecl *FD = getCurFunctionDecl()) { FnRetType = FD->getResultType(); - DeclaredRetType = FnRetType; if (FD->hasAttr() || FD->getType()->getAs()->getNoReturnAttr()) Diag(ReturnLoc, diag::warn_noreturn_function_has_return_expr) << FD->getDeclName(); } else if (ObjCMethodDecl *MD = getCurMethodDecl()) { - DeclaredRetType = MD->getResultType(); + FnRetType = MD->getResultType(); if (MD->hasRelatedResultType() && MD->getClassInterface()) { // In the implementation of a method with a related return type, the // type used to type-check the validity of return statements within the // method body is a pointer to the type of the class being implemented. - FnRetType = Context.getObjCInterfaceType(MD->getClassInterface()); - FnRetType = Context.getObjCObjectPointerType(FnRetType); - } else { - FnRetType = DeclaredRetType; + RelatedRetType = Context.getObjCInterfaceType(MD->getClassInterface()); + RelatedRetType = Context.getObjCObjectPointerType(RelatedRetType); } } else // If we don't have a function/method context, bail. return StmtError(); @@ -2045,6 +2042,21 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { if (!FnRetType->isDependentType() && !RetValExp->isTypeDependent()) { // we have a non-void function with an expression, continue checking + if (!RelatedRetType.isNull()) { + // If we have a related result type, perform an extra conversion here. + // FIXME: The diagnostics here don't really describe what is happening. + InitializedEntity Entity = + InitializedEntity::InitializeTemporary(RelatedRetType); + + ExprResult Res = PerformCopyInitialization(Entity, SourceLocation(), + RetValExp); + if (Res.isInvalid()) { + // FIXME: Cleanup temporaries here, anyway? + return StmtError(); + } + RetValExp = Res.takeAs(); + } + // C99 6.8.6.4p3(136): The return statement is not an assignment. The // overlap restriction of subclause 6.5.16.1 does not apply to the case of // function return. @@ -2068,17 +2080,6 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { } if (RetValExp) { - // If we type-checked an Objective-C method's return type based - // on a related return type, we may need to adjust the return - // type again. Do so now. - if (DeclaredRetType != FnRetType) { - ExprResult result = PerformImplicitConversion(RetValExp, - DeclaredRetType, - AA_Returning); - if (result.isInvalid()) return StmtError(); - RetValExp = result.take(); - } - CheckImplicitConversions(RetValExp, ReturnLoc); RetValExp = MaybeCreateExprWithCleanups(RetValExp); } diff --git a/clang/test/CodeGenObjC/arc.m b/clang/test/CodeGenObjC/arc.m index 1db00affe824..2a98b10909bb 100644 --- a/clang/test/CodeGenObjC/arc.m +++ b/clang/test/CodeGenObjC/arc.m @@ -640,9 +640,7 @@ void test22(_Bool cond) { // CHECK-NEXT: store i8* {{%.*}}, i8** [[CMD]] // CHECK-NEXT: [[T0:%.*]] = load [[TEST27]]** [[SELF]] // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST27]]* [[T0]] to i8* -// CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retain(i8* [[T1]]) -// CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] -// CHECK-NEXT: [[RET:%.*]] = bitcast +// CHECK-NEXT: [[RET:%.*]] = call i8* @objc_retain(i8* [[T1]]) // CHECK-NEXT: store i32 {{[0-9]+}}, i32* [[DEST]] // CHECK-NEXT: [[T0:%.*]] = load [[TEST27]]** [[SELF]] // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST27]]* [[T0]] to i8* @@ -706,9 +704,7 @@ static id _test29_allocator = 0; // Return statement. // CHECK-NEXT: [[T2:%.*]] = bitcast i8* [[CALL]] // CHECK-NEXT: [[CALL:%.*]] = bitcast -// CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retain(i8* [[CALL]]) nounwind -// CHECK-NEXT: [[T2:%.*]] = bitcast i8* [[T1]] -// CHECK-NEXT: [[RET:%.*]] = bitcast +// CHECK-NEXT: [[RET:%.*]] = call i8* @objc_retain(i8* [[CALL]]) nounwind // CHECK-NEXT: store i32 1, i32* [[CLEANUP]] // Cleanup. @@ -762,9 +758,7 @@ static id _test29_allocator = 0; // Return statement. // CHECK-NEXT: [[T0:%.*]] = load [[TEST29]]** [[SELF]] // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST29]]* [[T0]] to i8* -// CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retain(i8* [[T1]]) nounwind -// CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] -// CHECK-NEXT: [[RET:%.*]] = bitcast +// CHECK-NEXT: [[RET:%.*]] = call i8* @objc_retain(i8* [[T1]]) nounwind // CHECK-NEXT: store i32 1, i32* [[CLEANUP]] // Cleanup. @@ -819,9 +813,7 @@ char *helper; // Return. // CHECK-NEXT: [[T0:%.*]] = load [[TEST30]]** [[SELF]] // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST30]]* [[T0]] to i8* -// CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retain(i8* [[T1]]) -// CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] -// CHECK-NEXT: [[RET:%.*]] = bitcast +// CHECK-NEXT: [[RET:%.*]] = call i8* @objc_retain(i8* [[T1]]) // CHECK-NEXT: store i32 1 // Cleanup. diff --git a/clang/test/SemaObjC/instancetype.m b/clang/test/SemaObjC/instancetype.m index 13d6e0309f82..40f35d93b2bd 100644 --- a/clang/test/SemaObjC/instancetype.m +++ b/clang/test/SemaObjC/instancetype.m @@ -143,7 +143,7 @@ void test_instancetype_narrow_method_search() { @implementation Subclass4 + (id)alloc { - return self; // expected-warning{{incompatible pointer types returning 'Class' from a function with result type 'Subclass4 *'}} + return self; // expected-warning{{incompatible pointer types casting 'Class' to type 'Subclass4 *'}} } - (Subclass3 *)init { return 0; } // don't complain: we lost the related return type @@ -166,12 +166,12 @@ void test_instancetype_inherited() { @implementation Subclass2 - (instancetype)initSubclass2 { Subclass1 *sc1 = [[Subclass1 alloc] init]; - return sc1; // expected-warning{{incompatible pointer types returning 'Subclass1 *' from a function with result type 'Subclass2 *'}} + return sc1; // expected-warning{{incompatible pointer types casting 'Subclass1 *' to type 'Subclass2 *'}} } - (void)methodOnSubclass2 {} - (id)self { Subclass1 *sc1 = [[Subclass1 alloc] init]; - return sc1; // expected-warning{{incompatible pointer types returning 'Subclass1 *' from a function with result type 'Subclass2 *'}} + return sc1; // expected-warning{{incompatible pointer types casting 'Subclass1 *' to type 'Subclass2 *'}} } @end diff --git a/clang/test/SemaObjC/related-result-type-inference.m b/clang/test/SemaObjC/related-result-type-inference.m index addecbeecacf..1448dfdaa53a 100644 --- a/clang/test/SemaObjC/related-result-type-inference.m +++ b/clang/test/SemaObjC/related-result-type-inference.m @@ -169,3 +169,12 @@ void test_inference() { } @end +// PR12384 +@interface Fail @end +@protocol X @end +@implementation Fail +- (id) initWithX +{ + return (id)self; // expected-warning {{returning 'Fail *' from a function with incompatible result type 'id'}} +} +@end