-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[C] Add (new) -Wimplicit-void-ptr-cast to -Wc++-compat #136855
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-clang Author: Aaron Ballman (AaronBallman) ChangesThis 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 #17792 Full diff: https://github.com/llvm/llvm-project/pull/136855.diff 11 Files Affected:
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index bec670e573ca6..881c1c2f8b12d 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -140,6 +140,9 @@ C Language Changes
- Clang now allows an ``inline`` specifier on a typedef declaration of a
function type in Microsoft compatibility mode. #GH124869
- Clang now allows ``restrict`` qualifier for array types with pointer elements (#GH92847).
+- Added ``-Wimplicit-void-ptr-cast``, grouped under ``-Wc++-compat``, which
+ diagnoses implicit conversion from ``void *`` to another pointer type as
+ being incompatible with C++. (#GH17792)
C2y Feature Support
^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 59036b695da85..6441b8049ed8d 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -155,7 +155,8 @@ def C99Compat : DiagGroup<"c99-compat">;
def C23Compat : DiagGroup<"c23-compat">;
def : DiagGroup<"c2x-compat", [C23Compat]>;
-def CXXCompat: DiagGroup<"c++-compat">;
+def ImplicitVoidPtrCast : DiagGroup<"implicit-void-ptr-cast">;
+def CXXCompat: DiagGroup<"c++-compat", [ImplicitVoidPtrCast]>;
def ExternCCompat : DiagGroup<"extern-c-compat">;
def KeywordCompat : DiagGroup<"keyword-compat">;
def GNUCaseRange : DiagGroup<"gnu-case-range">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index c562802efba57..8ff170520aafe 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -8687,7 +8687,17 @@ def err_typecheck_missing_return_type_incompatible : Error<
"%diff{return type $ must match previous return type $|"
"return type must match previous return type}0,1 when %select{block "
"literal|lambda expression}2 has unspecified explicit return type">;
-
+def warn_compatible_implicit_pointer_conv : Warning<
+ "implicit conversion when %select{"
+ "%diff{assigning to $ from type $|assigning to type from type}0,1|"
+ "%diff{passing $ to parameter of type $|passing type to parameter of type}0,1|"
+ "%diff{returning $ from a function with result type $|returning type from a function with result type}0,1|"
+ "<CLANG BUG IF YOU SEE THIS>|" // converting
+ "%diff{initializing $ with an expression of type $|initializing type with an expression of type}0,1|"
+ "%diff{sending $ to parameter of type $|sending type to parameter of type}0,1|"
+ "<CLANG BUG IF YOU SEE THIS>" // casting
+ "}2 is not permitted in C++">,
+ InGroup<ImplicitVoidPtrCast>, DefaultIgnore;
def note_incomplete_class_and_qualified_id : Note<
"conformance of forward class %0 to protocol %1 cannot be confirmed">;
def warn_incompatible_qualified_id : Warning<
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 96d81e618494a..0c77c5b5ca30a 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -7786,6 +7786,11 @@ class Sema final : public SemaBase {
/// Compatible - the types are compatible according to the standard.
Compatible,
+ /// CompatibleVoidPtrToNonVoidPtr - The types are compatible in C because
+ /// a void * can implicitly convert to another pointer type, which we
+ /// differentiate for better diagnostic behavior.
+ CompatibleVoidPtrToNonVoidPtr,
+
/// PointerToInt - The assignment converts a pointer to an int, which we
/// accept as an extension.
PointerToInt,
@@ -7866,6 +7871,18 @@ class Sema final : public SemaBase {
Incompatible
};
+ bool IsAssignConvertCompatible(AssignConvertType ConvTy) {
+ switch (ConvTy) {
+ default:
+ return false;
+ case Compatible:
+ case CompatiblePointerDiscardsQualifiers:
+ case CompatibleVoidPtrToNonVoidPtr:
+ return true;
+ }
+ llvm_unreachable("impossible");
+ }
+
/// DiagnoseAssignmentResult - Emit a diagnostic, if required, for the
/// assignment conversion type specified by ConvTy. This returns true if the
/// conversion was invalid or false if the conversion was accepted.
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 3b5cf3661a52f..c960868badb52 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -3589,8 +3589,8 @@ static void handleCleanupAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
// If this ever proves to be a problem it should be easy to fix.
QualType Ty = S.Context.getPointerType(cast<VarDecl>(D)->getType());
QualType ParamTy = FD->getParamDecl(0)->getType();
- if (S.CheckAssignmentConstraints(FD->getParamDecl(0)->getLocation(),
- ParamTy, Ty) != Sema::Compatible) {
+ if (!S.IsAssignConvertCompatible(S.CheckAssignmentConstraints(
+ FD->getParamDecl(0)->getLocation(), ParamTy, Ty))) {
S.Diag(Loc, diag::err_attribute_cleanup_func_arg_incompatible_type)
<< NI.getName() << ParamTy << Ty;
return;
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 2e6ce17f8bf91..252d93e66a7e8 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -9062,8 +9062,12 @@ checkPointerTypesForAssignment(Sema &S, QualType LHSType, QualType RHSType,
}
if (rhptee->isVoidType()) {
+ // In C, void * to another pointer type is compatible, but we want to note
+ // that there will be an implicit conversion happening here.
if (lhptee->isIncompleteOrObjectType())
- return ConvTy;
+ return ConvTy == Sema::Compatible && !S.getLangOpts().CPlusPlus
+ ? Sema::CompatibleVoidPtrToNonVoidPtr
+ : ConvTy;
// As an extension, we allow cast to/from void* to function pointer.
assert(lhptee->isFunctionType());
@@ -9098,7 +9102,7 @@ checkPointerTypesForAssignment(Sema &S, QualType LHSType, QualType RHSType,
// Types are compatible ignoring the sign. Qualifier incompatibility
// takes priority over sign incompatibility because the sign
// warning can be disabled.
- if (ConvTy != Sema::Compatible)
+ if (!S.IsAssignConvertCompatible(ConvTy))
return ConvTy;
return Sema::IncompatiblePointerSign;
@@ -16980,7 +16984,11 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy,
case Compatible:
DiagnoseAssignmentEnum(DstType, SrcType, SrcExpr);
return false;
-
+ case CompatibleVoidPtrToNonVoidPtr:
+ // Still a valid conversion, but we may want to diagnose for C++
+ // compatibility reasons.
+ DiagKind = diag::warn_compatible_implicit_pointer_conv;
+ break;
case PointerToInt:
if (getLangOpts().CPlusPlus) {
DiagKind = diag::err_typecheck_convert_pointer_int;
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 0910a820438b0..f04a154bcfd5f 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -8328,10 +8328,9 @@ ExprResult InitializationSequence::Perform(Sema &S,
// If this is a call, allow conversion to a transparent union.
ExprResult CurInitExprRes = CurInit;
- if (ConvTy != Sema::Compatible &&
- Entity.isParameterKind() &&
- S.CheckTransparentUnionArgumentConstraints(Step->Type, CurInitExprRes)
- == Sema::Compatible)
+ if (!S.IsAssignConvertCompatible(ConvTy) && Entity.isParameterKind() &&
+ S.CheckTransparentUnionArgumentConstraints(
+ Step->Type, CurInitExprRes) == Sema::Compatible)
ConvTy = Sema::Compatible;
if (CurInitExprRes.isInvalid())
return ExprError();
diff --git a/clang/lib/Sema/SemaObjC.cpp b/clang/lib/Sema/SemaObjC.cpp
index 9b24b5f052119..eba4a7cb6010c 100644
--- a/clang/lib/Sema/SemaObjC.cpp
+++ b/clang/lib/Sema/SemaObjC.cpp
@@ -2341,8 +2341,8 @@ static void checkCollectionLiteralElement(Sema &S, QualType TargetElementType,
QualType ElementType = Element->getType();
ExprResult ElementResult(Element);
if (ElementType->getAs<ObjCObjectPointerType>() &&
- S.CheckSingleAssignmentConstraints(TargetElementType, ElementResult,
- false, false) != Sema::Compatible) {
+ !S.IsAssignConvertCompatible(S.CheckSingleAssignmentConstraints(
+ TargetElementType, ElementResult, false, false))) {
S.Diag(Element->getBeginLoc(), diag::warn_objc_collection_literal_element)
<< ElementType << ElementKind << TargetElementType
<< Element->getSourceRange();
diff --git a/clang/lib/Sema/SemaObjCProperty.cpp b/clang/lib/Sema/SemaObjCProperty.cpp
index f37982eddace9..3e962fcb8b0e5 100644
--- a/clang/lib/Sema/SemaObjCProperty.cpp
+++ b/clang/lib/Sema/SemaObjCProperty.cpp
@@ -1349,9 +1349,9 @@ Decl *SemaObjC::ActOnPropertyImplDecl(
PropertyIvarType->castAs<ObjCObjectPointerType>(),
IvarType->castAs<ObjCObjectPointerType>());
else {
- compat = (SemaRef.CheckAssignmentConstraints(
- PropertyIvarLoc, PropertyIvarType, IvarType) ==
- Sema::Compatible);
+ compat = SemaRef.IsAssignConvertCompatible(
+ SemaRef.CheckAssignmentConstraints(PropertyIvarLoc,
+ PropertyIvarType, IvarType));
}
if (!compat) {
Diag(PropertyDiagLoc, diag::err_property_ivar_type)
@@ -1702,8 +1702,9 @@ bool SemaObjC::DiagnosePropertyAccessorMismatch(ObjCPropertyDecl *property,
PropertyRValueType->getAs<ObjCObjectPointerType>()) &&
(getterObjCPtr = GetterType->getAs<ObjCObjectPointerType>()))
compat = Context.canAssignObjCInterfaces(getterObjCPtr, propertyObjCPtr);
- else if (SemaRef.CheckAssignmentConstraints(
- Loc, GetterType, PropertyRValueType) != Sema::Compatible) {
+ else if (!SemaRef.IsAssignConvertCompatible(
+ SemaRef.CheckAssignmentConstraints(Loc, GetterType,
+ PropertyRValueType))) {
Diag(Loc, diag::err_property_accessor_type)
<< property->getDeclName() << PropertyRValueType
<< GetterMethod->getSelector() << GetterType;
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 5b224b6c08fef..a2ea4bb9afe15 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -2518,6 +2518,7 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
ImplicitConversionKind SecondConv;
switch (Conv) {
case Sema::Compatible:
+ case Sema::CompatibleVoidPtrToNonVoidPtr: // __attribute__((overloadable))
SecondConv = ICK_C_Only_Conversion;
break;
// For our purposes, discarding qualifiers is just as bad as using an
diff --git a/clang/test/Sema/implicit-void-ptr-cast.c b/clang/test/Sema/implicit-void-ptr-cast.c
new file mode 100644
index 0000000000000..bf981c51e724e
--- /dev/null
+++ b/clang/test/Sema/implicit-void-ptr-cast.c
@@ -0,0 +1,28 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wimplicit-void-ptr-cast %s
+// RUN: %clang_cc1 -fsyntax-only -verify -Wc++-compat %s
+// RUN: %clang_cc1 -fsyntax-only -verify=good %s
+// RUN: %clang_cc1 -fsyntax-only -verify=good -Wc++-compat -Wno-implicit-void-ptr-cast %s
+// good-no-diagnostics
+
+typedef __typeof__(sizeof(int)) size_t;
+extern void *malloc(size_t);
+
+void func(int *); // expected-note {{passing argument to parameter here}}
+
+void test(void) {
+ int *x = malloc(sizeof(char)); // expected-warning {{implicit conversion when initializing 'int *' with an expression of type 'void *' is not permitted in C++}}
+ x = malloc(sizeof(char)); // expected-warning {{implicit conversion when assigning to 'int *' from type 'void *' is not permitted in C++}}
+ func(malloc(sizeof(char))); // expected-warning {{implicit conversion when passing 'void *' to parameter of type 'int *' is not permitted in C++}}
+ x = (int *)malloc(sizeof(char));
+
+ void *vp = 0;
+ x = vp; // expected-warning {{implicit conversion when assigning to 'int *' from type 'void *' is not permitted in C++}}
+ vp = vp;
+
+ x = (void *)malloc(sizeof(char)); // expected-warning {{implicit conversion when assigning to 'int *' from type 'void *' is not permitted in C++}}
+ const int *y = vp; // expected-warning {{implicit conversion when initializing 'const int *' with an expression of type 'void *' is not permitted in C++}}
+}
+
+int *other_func(void *ptr) {
+ return ptr; // expected-warning {{implicit conversion when returning 'void *' from a function with result type 'int *' is not permitted in C++}}
+}
|
Use a bookmark, per review request Add C++ RUN line to show we diagnose invalid constructs in C++
Could you please take a look into the issue #138145 i created and provide an input from your side if possible. |
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
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
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
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
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 #17792