Make sure we perform the relevant implied conversions correctly for ObjC methods with related result types. PR12384.

llvm-svn: 153716
This commit is contained in:
Eli Friedman 2012-03-30 01:13:43 +00:00
parent f4d006348b
commit 410fc7ae89
4 changed files with 35 additions and 33 deletions

View File

@ -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<NoReturnAttr>() ||
FD->getType()->getAs<FunctionType>()->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<Expr>();
}
// 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);
}

View File

@ -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.

View File

@ -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

View File

@ -169,3 +169,12 @@ void test_inference() {
}
@end
// PR12384
@interface Fail @end
@protocol X @end
@implementation Fail
- (id<X>) initWithX
{
return (id)self; // expected-warning {{returning 'Fail *' from a function with incompatible result type 'id<X>'}}
}
@end