Skip to content

Commit bc0fa9a

Browse files
AaronBallmanIanWood1
authored andcommitted
[C] Add (new) -Wimplicit-void-ptr-cast to -Wc++-compat (llvm#136855)
This introduces a new diagnostic group (-Wimplicit-void-ptr-cast), grouped under -Wc++-compat, which diagnoses implicit conversions from void * to another pointer type in C. It's a common source of incompatibility with C++ and is something GCC diagnoses (though GCC does not have a specific warning group for this). Fixes llvm#17792
1 parent 6b73a0d commit bc0fa9a

File tree

11 files changed

+96
-18
lines changed

11 files changed

+96
-18
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,9 @@ C Language Changes
140140
- Clang now allows an ``inline`` specifier on a typedef declaration of a
141141
function type in Microsoft compatibility mode. #GH124869
142142
- Clang now allows ``restrict`` qualifier for array types with pointer elements (#GH92847).
143+
- Added ``-Wimplicit-void-ptr-cast``, grouped under ``-Wc++-compat``, which
144+
diagnoses implicit conversion from ``void *`` to another pointer type as
145+
being incompatible with C++. (#GH17792)
143146

144147
C2y Feature Support
145148
^^^^^^^^^^^^^^^^^^^

clang/include/clang/Basic/DiagnosticGroups.td

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,8 @@ def C99Compat : DiagGroup<"c99-compat">;
155155
def C23Compat : DiagGroup<"c23-compat">;
156156
def : DiagGroup<"c2x-compat", [C23Compat]>;
157157

158-
def CXXCompat: DiagGroup<"c++-compat">;
158+
def ImplicitVoidPtrCast : DiagGroup<"implicit-void-ptr-cast">;
159+
def CXXCompat: DiagGroup<"c++-compat", [ImplicitVoidPtrCast]>;
159160
def ExternCCompat : DiagGroup<"extern-c-compat">;
160161
def KeywordCompat : DiagGroup<"keyword-compat">;
161162
def GNUCaseRange : DiagGroup<"gnu-case-range">;

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8687,7 +8687,17 @@ def err_typecheck_missing_return_type_incompatible : Error<
86878687
"%diff{return type $ must match previous return type $|"
86888688
"return type must match previous return type}0,1 when %select{block "
86898689
"literal|lambda expression}2 has unspecified explicit return type">;
8690-
8690+
def warn_compatible_implicit_pointer_conv : Warning<
8691+
"implicit conversion when %select{"
8692+
"%diff{assigning to $ from type $|assigning to type from type}0,1|"
8693+
"%diff{passing $ to parameter of type $|passing type to parameter of type}0,1|"
8694+
"%diff{returning $ from a function with result type $|returning type from a function with result type}0,1|"
8695+
"<CLANG BUG IF YOU SEE THIS>|" // converting
8696+
"%diff{initializing $ with an expression of type $|initializing type with an expression of type}0,1|"
8697+
"%diff{sending $ to parameter of type $|sending type to parameter of type}0,1|"
8698+
"<CLANG BUG IF YOU SEE THIS>" // casting
8699+
"}2 is not permitted in C++">,
8700+
InGroup<ImplicitVoidPtrCast>, DefaultIgnore;
86918701
def note_incomplete_class_and_qualified_id : Note<
86928702
"conformance of forward class %0 to protocol %1 cannot be confirmed">;
86938703
def warn_incompatible_qualified_id : Warning<

clang/include/clang/Sema/Sema.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7786,6 +7786,11 @@ class Sema final : public SemaBase {
77867786
/// Compatible - the types are compatible according to the standard.
77877787
Compatible,
77887788

7789+
/// CompatibleVoidPtrToNonVoidPtr - The types are compatible in C because
7790+
/// a void * can implicitly convert to another pointer type, which we
7791+
/// differentiate for better diagnostic behavior.
7792+
CompatibleVoidPtrToNonVoidPtr,
7793+
77897794
/// PointerToInt - The assignment converts a pointer to an int, which we
77907795
/// accept as an extension.
77917796
PointerToInt,
@@ -7866,6 +7871,18 @@ class Sema final : public SemaBase {
78667871
Incompatible
78677872
};
78687873

7874+
bool IsAssignConvertCompatible(AssignConvertType ConvTy) {
7875+
switch (ConvTy) {
7876+
default:
7877+
return false;
7878+
case Compatible:
7879+
case CompatiblePointerDiscardsQualifiers:
7880+
case CompatibleVoidPtrToNonVoidPtr:
7881+
return true;
7882+
}
7883+
llvm_unreachable("impossible");
7884+
}
7885+
78697886
/// DiagnoseAssignmentResult - Emit a diagnostic, if required, for the
78707887
/// assignment conversion type specified by ConvTy. This returns true if the
78717888
/// conversion was invalid or false if the conversion was accepted.

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3589,8 +3589,8 @@ static void handleCleanupAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
35893589
// If this ever proves to be a problem it should be easy to fix.
35903590
QualType Ty = S.Context.getPointerType(cast<VarDecl>(D)->getType());
35913591
QualType ParamTy = FD->getParamDecl(0)->getType();
3592-
if (S.CheckAssignmentConstraints(FD->getParamDecl(0)->getLocation(),
3593-
ParamTy, Ty) != Sema::Compatible) {
3592+
if (!S.IsAssignConvertCompatible(S.CheckAssignmentConstraints(
3593+
FD->getParamDecl(0)->getLocation(), ParamTy, Ty))) {
35943594
S.Diag(Loc, diag::err_attribute_cleanup_func_arg_incompatible_type)
35953595
<< NI.getName() << ParamTy << Ty;
35963596
return;

clang/lib/Sema/SemaExpr.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9062,8 +9062,12 @@ checkPointerTypesForAssignment(Sema &S, QualType LHSType, QualType RHSType,
90629062
}
90639063

90649064
if (rhptee->isVoidType()) {
9065+
// In C, void * to another pointer type is compatible, but we want to note
9066+
// that there will be an implicit conversion happening here.
90659067
if (lhptee->isIncompleteOrObjectType())
9066-
return ConvTy;
9068+
return ConvTy == Sema::Compatible && !S.getLangOpts().CPlusPlus
9069+
? Sema::CompatibleVoidPtrToNonVoidPtr
9070+
: ConvTy;
90679071

90689072
// As an extension, we allow cast to/from void* to function pointer.
90699073
assert(lhptee->isFunctionType());
@@ -9098,7 +9102,7 @@ checkPointerTypesForAssignment(Sema &S, QualType LHSType, QualType RHSType,
90989102
// Types are compatible ignoring the sign. Qualifier incompatibility
90999103
// takes priority over sign incompatibility because the sign
91009104
// warning can be disabled.
9101-
if (ConvTy != Sema::Compatible)
9105+
if (!S.IsAssignConvertCompatible(ConvTy))
91029106
return ConvTy;
91039107

91049108
return Sema::IncompatiblePointerSign;
@@ -16980,7 +16984,11 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy,
1698016984
case Compatible:
1698116985
DiagnoseAssignmentEnum(DstType, SrcType, SrcExpr);
1698216986
return false;
16983-
16987+
case CompatibleVoidPtrToNonVoidPtr:
16988+
// Still a valid conversion, but we may want to diagnose for C++
16989+
// compatibility reasons.
16990+
DiagKind = diag::warn_compatible_implicit_pointer_conv;
16991+
break;
1698416992
case PointerToInt:
1698516993
if (getLangOpts().CPlusPlus) {
1698616994
DiagKind = diag::err_typecheck_convert_pointer_int;

clang/lib/Sema/SemaInit.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8328,10 +8328,9 @@ ExprResult InitializationSequence::Perform(Sema &S,
83288328

83298329
// If this is a call, allow conversion to a transparent union.
83308330
ExprResult CurInitExprRes = CurInit;
8331-
if (ConvTy != Sema::Compatible &&
8332-
Entity.isParameterKind() &&
8333-
S.CheckTransparentUnionArgumentConstraints(Step->Type, CurInitExprRes)
8334-
== Sema::Compatible)
8331+
if (!S.IsAssignConvertCompatible(ConvTy) && Entity.isParameterKind() &&
8332+
S.CheckTransparentUnionArgumentConstraints(
8333+
Step->Type, CurInitExprRes) == Sema::Compatible)
83358334
ConvTy = Sema::Compatible;
83368335
if (CurInitExprRes.isInvalid())
83378336
return ExprError();

clang/lib/Sema/SemaObjC.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2341,8 +2341,8 @@ static void checkCollectionLiteralElement(Sema &S, QualType TargetElementType,
23412341
QualType ElementType = Element->getType();
23422342
ExprResult ElementResult(Element);
23432343
if (ElementType->getAs<ObjCObjectPointerType>() &&
2344-
S.CheckSingleAssignmentConstraints(TargetElementType, ElementResult,
2345-
false, false) != Sema::Compatible) {
2344+
!S.IsAssignConvertCompatible(S.CheckSingleAssignmentConstraints(
2345+
TargetElementType, ElementResult, false, false))) {
23462346
S.Diag(Element->getBeginLoc(), diag::warn_objc_collection_literal_element)
23472347
<< ElementType << ElementKind << TargetElementType
23482348
<< Element->getSourceRange();

clang/lib/Sema/SemaObjCProperty.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1349,9 +1349,9 @@ Decl *SemaObjC::ActOnPropertyImplDecl(
13491349
PropertyIvarType->castAs<ObjCObjectPointerType>(),
13501350
IvarType->castAs<ObjCObjectPointerType>());
13511351
else {
1352-
compat = (SemaRef.CheckAssignmentConstraints(
1353-
PropertyIvarLoc, PropertyIvarType, IvarType) ==
1354-
Sema::Compatible);
1352+
compat = SemaRef.IsAssignConvertCompatible(
1353+
SemaRef.CheckAssignmentConstraints(PropertyIvarLoc,
1354+
PropertyIvarType, IvarType));
13551355
}
13561356
if (!compat) {
13571357
Diag(PropertyDiagLoc, diag::err_property_ivar_type)
@@ -1702,8 +1702,9 @@ bool SemaObjC::DiagnosePropertyAccessorMismatch(ObjCPropertyDecl *property,
17021702
PropertyRValueType->getAs<ObjCObjectPointerType>()) &&
17031703
(getterObjCPtr = GetterType->getAs<ObjCObjectPointerType>()))
17041704
compat = Context.canAssignObjCInterfaces(getterObjCPtr, propertyObjCPtr);
1705-
else if (SemaRef.CheckAssignmentConstraints(
1706-
Loc, GetterType, PropertyRValueType) != Sema::Compatible) {
1705+
else if (!SemaRef.IsAssignConvertCompatible(
1706+
SemaRef.CheckAssignmentConstraints(Loc, GetterType,
1707+
PropertyRValueType))) {
17071708
Diag(Loc, diag::err_property_accessor_type)
17081709
<< property->getDeclName() << PropertyRValueType
17091710
<< GetterMethod->getSelector() << GetterType;

clang/lib/Sema/SemaOverload.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2518,6 +2518,7 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
25182518
ImplicitConversionKind SecondConv;
25192519
switch (Conv) {
25202520
case Sema::Compatible:
2521+
case Sema::CompatibleVoidPtrToNonVoidPtr: // __attribute__((overloadable))
25212522
SecondConv = ICK_C_Only_Conversion;
25222523
break;
25232524
// For our purposes, discarding qualifiers is just as bad as using an
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify=c -Wimplicit-void-ptr-cast %s
2+
// RUN: %clang_cc1 -fsyntax-only -verify=c -Wc++-compat %s
3+
// RUN: %clang_cc1 -fsyntax-only -verify=cxx -x c++ %s
4+
// RUN: %clang_cc1 -fsyntax-only -verify=good %s
5+
// RUN: %clang_cc1 -fsyntax-only -verify=good -Wc++-compat -Wno-implicit-void-ptr-cast %s
6+
// good-no-diagnostics
7+
8+
typedef __typeof__(sizeof(int)) size_t;
9+
extern void *malloc(size_t);
10+
11+
void func(int *); // #func-param
12+
13+
void test(void) {
14+
int *x = malloc(sizeof(char)); // c-warning {{implicit conversion when initializing 'int *' with an expression of type 'void *' is not permitted in C++}} \
15+
cxx-error {{cannot initialize a variable of type 'int *' with an rvalue of type 'void *'}}
16+
x = malloc(sizeof(char)); // c-warning {{implicit conversion when assigning to 'int *' from type 'void *' is not permitted in C++}} \
17+
cxx-error {{assigning to 'int *' from incompatible type 'void *'}}
18+
func(malloc(sizeof(char))); // c-warning {{implicit conversion when passing 'void *' to parameter of type 'int *' is not permitted in C++}} \
19+
c-note@#func-param {{passing argument to parameter here}} \
20+
cxx-error {{no matching function for call to 'func'}} \
21+
cxx-note@#func-param {{candidate function not viable: cannot convert argument of incomplete type 'void *' to 'int *' for 1st argument}}
22+
x = (int *)malloc(sizeof(char));
23+
24+
void *vp = 0;
25+
x = vp; // c-warning {{implicit conversion when assigning to 'int *' from type 'void *' is not permitted in C++}} \
26+
cxx-error {{assigning to 'int *' from incompatible type 'void *'}}
27+
vp = vp;
28+
29+
x = (void *)malloc(sizeof(char)); // c-warning {{implicit conversion when assigning to 'int *' from type 'void *' is not permitted in C++}} \
30+
cxx-error {{assigning to 'int *' from incompatible type 'void *'}}
31+
const int *y = vp; // c-warning {{implicit conversion when initializing 'const int *' with an expression of type 'void *' is not permitted in C++}} \
32+
cxx-error {{cannot initialize a variable of type 'const int *' with an lvalue of type 'void *'}}
33+
}
34+
35+
int *other_func(void *ptr) {
36+
return ptr; // c-warning {{implicit conversion when returning 'void *' from a function with result type 'int *' is not permitted in C++}} \
37+
cxx-error {{cannot initialize return object of type 'int *' with an lvalue of type 'void *'}}
38+
}

0 commit comments

Comments
 (0)