Skip to content

Commit 422080f

Browse files
committed
Pretend that enum constants have enum type when inferring a block return type.
In C, enum constants have the type of the enum's underlying integer type, rather than the type of the enum. (This is not true in C++.) This leads to odd warnings when returning enum constants directly in blocks with inferred return types. The easiest way out of this is to pretend that, like C++, enum constants have enum type when being returned from a block. <rdar://problem/11662489> llvm-svn: 158899
1 parent 815fe26 commit 422080f

File tree

2 files changed

+152
-3
lines changed

2 files changed

+152
-3
lines changed

clang/lib/Sema/SemaStmt.cpp

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2128,10 +2128,32 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
21282128
return StmtError();
21292129
RetValExp = Result.take();
21302130

2131-
if (!RetValExp->isTypeDependent())
2131+
if (!RetValExp->isTypeDependent()) {
21322132
ReturnT = RetValExp->getType();
2133-
else
2133+
2134+
// In C, enum constants have the type of their underlying integer type,
2135+
// not the enum. When inferring block return values, we should infer
2136+
// the enum type if an enum constant is used, unless the enum is
2137+
// anonymous (in which case there can be no variables of its type).
2138+
if (!getLangOpts().CPlusPlus) {
2139+
Expr *InsideExpr = RetValExp->IgnoreParenImpCasts();
2140+
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(InsideExpr)) {
2141+
Decl *D = DRE->getDecl();
2142+
if (EnumConstantDecl *ECD = dyn_cast<EnumConstantDecl>(D)) {
2143+
EnumDecl *Enum = cast<EnumDecl>(ECD->getDeclContext());
2144+
if (Enum->getDeclName() || Enum->getTypedefNameForAnonDecl()) {
2145+
ReturnT = Context.getTypeDeclType(Enum);
2146+
ExprResult Casted = ImpCastExprToType(RetValExp, ReturnT,
2147+
CK_IntegralCast);
2148+
assert(Casted.isUsable());
2149+
RetValExp = Casted.take();
2150+
}
2151+
}
2152+
}
2153+
}
2154+
} else {
21342155
ReturnT = Context.DependentTy;
2156+
}
21352157
} else {
21362158
if (RetValExp) {
21372159
// C++11 [expr.lambda.prim]p4 bans inferring the result from an
@@ -2147,7 +2169,7 @@ Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
21472169
if (!CurCap->ReturnType.isNull() &&
21482170
!CurCap->ReturnType->isDependentType() &&
21492171
!ReturnT->isDependentType() &&
2150-
!Context.hasSameType(ReturnT, CurCap->ReturnType)) {
2172+
!Context.hasSameType(ReturnT, CurCap->ReturnType)) {
21512173
Diag(ReturnLoc, diag::err_typecheck_missing_return_type_incompatible)
21522174
<< ReturnT << CurCap->ReturnType
21532175
<< (getCurLambda() != 0);

clang/test/SemaObjC/blocks.m

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,130 @@ void foo10() {
7373
NSLog(@"%@", myBlock);
7474
}
7575

76+
77+
// In C, enum constants have the type of the underlying integer type, not the
78+
// enumeration they are part of. We pretend the constants have enum type when
79+
// inferring block return types, so that they can be mixed-and-matched with
80+
// other expressions of enum type.
81+
enum CStyleEnum {
82+
CSE_Value = 1
83+
};
84+
enum CStyleEnum getCSE();
85+
typedef enum CStyleEnum (^cse_block_t)();
86+
87+
void testCStyleEnumInference(bool arg) {
88+
cse_block_t a;
89+
90+
// No warnings here.
91+
a = ^{ return CSE_Value; };
92+
a = ^{ return getCSE(); };
93+
94+
a = ^{ // expected-error {{incompatible block pointer types assigning to 'cse_block_t' (aka 'enum CStyleEnum (^)()') from 'int (^)(void)'}}
95+
return 1;
96+
};
97+
98+
// No warnings here.
99+
a = ^{ if (arg) return CSE_Value; else return CSE_Value; };
100+
a = ^{ if (arg) return getCSE(); else return getCSE(); };
101+
a = ^{ if (arg) return CSE_Value; else return getCSE(); };
102+
a = ^{ if (arg) return getCSE(); else return CSE_Value; };
103+
104+
// Technically these two blocks should return 'int'.
105+
// The first case is easy to handle -- just don't cast the enum constant
106+
// to the enum type. However, the second guess would require going back
107+
// and REMOVING the cast from the first return statement, which isn't really
108+
// feasible (there may be more than one previous return statement with enum
109+
// type). For symmetry, we just treat them the same way.
110+
a = ^{ // expected-error {{incompatible block pointer types assigning to 'cse_block_t' (aka 'enum CStyleEnum (^)()') from 'int (^)(void)'}}
111+
if (arg)
112+
return 1;
113+
else
114+
return CSE_Value; // expected-error {{return type 'enum CStyleEnum' must match previous return type 'int'}}
115+
};
116+
117+
a = ^{
118+
if (arg)
119+
return CSE_Value;
120+
else
121+
return 1; // expected-error {{return type 'int' must match previous return type 'enum CStyleEnum'}}
122+
};
123+
}
124+
125+
126+
enum FixedTypeEnum : unsigned {
127+
FTE_Value = 1U
128+
};
129+
enum FixedTypeEnum getFTE();
130+
typedef enum FixedTypeEnum (^fte_block_t)();
131+
132+
void testFixedTypeEnumInference(bool arg) {
133+
fte_block_t a;
134+
135+
// No warnings here.
136+
a = ^{ return FTE_Value; };
137+
a = ^{ return getFTE(); };
138+
139+
// Since we fixed the underlying type of the enum, this is considered a
140+
// compatible block type.
141+
a = ^{
142+
return 1U;
143+
};
144+
145+
// No warnings here.
146+
a = ^{ if (arg) return FTE_Value; else return FTE_Value; };
147+
a = ^{ if (arg) return getFTE(); else return getFTE(); };
148+
a = ^{ if (arg) return FTE_Value; else return getFTE(); };
149+
a = ^{ if (arg) return getFTE(); else return FTE_Value; };
150+
151+
// Technically these two blocks should return 'unsigned'.
152+
// The first case is easy to handle -- just don't cast the enum constant
153+
// to the enum type. However, the second guess would require going back
154+
// and REMOVING the cast from the first return statement, which isn't really
155+
// feasible (there may be more than one previous return statement with enum
156+
// type). For symmetry, we just treat them the same way.
157+
a = ^{
158+
if (arg)
159+
return 1U;
160+
else
161+
return FTE_Value; // expected-error{{return type 'enum FixedTypeEnum' must match previous return type 'unsigned int'}}
162+
};
163+
164+
a = ^{
165+
if (arg)
166+
return FTE_Value;
167+
else
168+
return 1U; // expected-error{{return type 'unsigned int' must match previous return type 'enum FixedTypeEnum'}}
169+
};
170+
}
171+
172+
173+
enum {
174+
AnonymousValue = 1
175+
};
176+
177+
enum : short {
178+
FixedAnonymousValue = 1
179+
};
180+
181+
typedef enum {
182+
TDE_Value
183+
} TypeDefEnum;
184+
185+
typedef enum : short {
186+
TDFTE_Value
187+
} TypeDefFixedTypeEnum;
188+
189+
190+
typedef int (^int_block_t)();
191+
typedef short (^short_block_t)();
192+
void testAnonymousEnumTypes() {
193+
int_block_t IB;
194+
IB = ^{ return AnonymousValue; };
195+
IB = ^{ return TDE_Value; }; // expected-error {{incompatible block pointer types assigning to 'int_block_t' (aka 'int (^)()') from 'TypeDefEnum (^)(void)'}}
196+
IB = ^{ return CSE_Value; }; // expected-error {{incompatible block pointer types assigning to 'int_block_t' (aka 'int (^)()') from 'enum CStyleEnum (^)(void)'}}
197+
198+
short_block_t SB;
199+
SB = ^{ return FixedAnonymousValue; };
200+
// This is not an error anyway since the enum has a fixed underlying type.
201+
SB = ^{ return TDFTE_Value; };
202+
}

0 commit comments

Comments
 (0)