forked from OSchip/llvm-project
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:
parent
f4d006348b
commit
410fc7ae89
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue