Skip to content

Commit 367b84e

Browse files
Vladislav AranovVladislav Aranov
authored andcommitted
Fixing array-to-pointer decay induced issue with conversion of the
multidimentional arrays in C language.
1 parent dcce216 commit 367b84e

File tree

2 files changed

+187
-65
lines changed

2 files changed

+187
-65
lines changed

clang/lib/Sema/SemaExpr.cpp

Lines changed: 135 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -9032,15 +9032,107 @@ static bool IsInvalidCmseNSCallConversion(Sema &S, QualType FromType,
90329032
return false;
90339033
}
90349034

9035+
/// This helper function returns element type if QT is a multidimentional array
9036+
static std::optional<QualType> getMultiDimentionalArrayType(Sema const *S,
9037+
QualType QT) {
9038+
const ArrayType *AT = S->Context.getAsArrayType(QT);
9039+
if (AT) {
9040+
const ConstantArrayType *CAT = llvm::dyn_cast<ConstantArrayType>(AT);
9041+
if (CAT) {
9042+
QualType ElementType = CAT->getElementType();
9043+
while (CAT) {
9044+
llvm::APInt Size = CAT->getSize();
9045+
// Zero sized array are no good.
9046+
if (Size == 0)
9047+
return std::optional<QualType>();
9048+
ElementType = CAT->getElementType();
9049+
CAT = llvm::dyn_cast<ConstantArrayType>(ElementType);
9050+
}
9051+
return std::optional<QualType>(
9052+
QualType(std::get<const Type *>(ElementType.split().asPair()), 0));
9053+
}
9054+
}
9055+
return std::optional<QualType>();
9056+
}
9057+
9058+
/// This helper function return AssignConvertType if pointers are not
9059+
/// compatible
9060+
static std::optional<AssignConvertType> checkPointerAssignmentCompatibility(
9061+
Sema &S, const Type *lhptee, const Type *rhptee, QualType ltrans,
9062+
QualType rtrans, QualType LHSType, QualType RHSType,
9063+
AssignConvertType ConvTy) {
9064+
if (!S.Context.typesAreCompatible(ltrans, rtrans)) {
9065+
// Check if the pointee types are compatible ignoring the sign.
9066+
// We explicitly check for char so that we catch "char" vs
9067+
// "unsigned char" on systems where "char" is unsigned.
9068+
if (lhptee->isCharType())
9069+
ltrans = S.Context.UnsignedCharTy;
9070+
else if (lhptee->hasSignedIntegerRepresentation())
9071+
ltrans = S.Context.getCorrespondingUnsignedType(ltrans);
9072+
9073+
if (rhptee->isCharType())
9074+
rtrans = S.Context.UnsignedCharTy;
9075+
else if (rhptee->hasSignedIntegerRepresentation())
9076+
rtrans = S.Context.getCorrespondingUnsignedType(rtrans);
9077+
9078+
if (ltrans == rtrans) {
9079+
// Types are compatible ignoring the sign. Qualifier incompatibility
9080+
// takes priority over sign incompatibility because the sign
9081+
// warning can be disabled.
9082+
if (!S.IsAssignConvertCompatible(ConvTy))
9083+
return ConvTy;
9084+
9085+
return AssignConvertType::IncompatiblePointerSign;
9086+
}
9087+
9088+
// If we are a multi-level pointer, it's possible that our issue is simply
9089+
// one of qualification - e.g. char ** -> const char ** is not allowed. If
9090+
// the eventual target type is the same and the pointers have the same
9091+
// level of indirection, this must be the issue.
9092+
if (isa<PointerType>(lhptee) && isa<PointerType>(rhptee)) {
9093+
Qualifiers lhq, rhq;
9094+
do {
9095+
std::tie(lhptee, lhq) =
9096+
cast<PointerType>(lhptee)->getPointeeType().split().asPair();
9097+
std::tie(rhptee, rhq) =
9098+
cast<PointerType>(rhptee)->getPointeeType().split().asPair();
9099+
9100+
// Inconsistent address spaces at this point is invalid, even if the
9101+
// address spaces would be compatible.
9102+
// FIXME: This doesn't catch address space mismatches for pointers of
9103+
// different nesting levels, like:
9104+
// __local int *** a;
9105+
// int ** b = a;
9106+
// It's not clear how to actually determine when such pointers are
9107+
// invalidly incompatible.
9108+
if (lhq.getAddressSpace() != rhq.getAddressSpace())
9109+
return AssignConvertType::
9110+
IncompatibleNestedPointerAddressSpaceMismatch;
9111+
9112+
} while (isa<PointerType>(lhptee) && isa<PointerType>(rhptee));
9113+
9114+
if (lhptee == rhptee)
9115+
return AssignConvertType::IncompatibleNestedPointerQualifiers;
9116+
}
9117+
9118+
// General pointer incompatibility takes priority over qualifiers.
9119+
if (RHSType->isFunctionPointerType() && LHSType->isFunctionPointerType())
9120+
return AssignConvertType::IncompatibleFunctionPointer;
9121+
9122+
// Return pointers as incompatible
9123+
return AssignConvertType::IncompatiblePointer;
9124+
}
9125+
return std::optional<AssignConvertType>();
9126+
}
9127+
90359128
// checkPointerTypesForAssignment - This is a very tricky routine (despite
90369129
// being closely modeled after the C99 spec:-). The odd characteristic of this
90379130
// routine is it effectively iqnores the qualifiers on the top level pointee.
90389131
// This circumvents the usual type rules specified in 6.2.7p1 & 6.7.5.[1-3].
90399132
// FIXME: add a couple examples in this comment.
9040-
static AssignConvertType checkPointerTypesForAssignment(Sema &S,
9041-
QualType LHSType,
9042-
QualType RHSType,
9043-
SourceLocation Loc) {
9133+
static AssignConvertType
9134+
checkPointerTypesForAssignment(Sema &S, QualType LHSType, QualType RHSType,
9135+
QualType OrigRHSType, SourceLocation Loc) {
90449136
assert(LHSType.isCanonical() && "LHS not canonicalized!");
90459137
assert(RHSType.isCanonical() && "RHS not canonicalized!");
90469138

@@ -9128,65 +9220,26 @@ static AssignConvertType checkPointerTypesForAssignment(Sema &S,
91289220

91299221
// C99 6.5.16.1p1 (constraint 3): both operands are pointers to qualified or
91309222
// unqualified versions of compatible types, ...
9131-
QualType ltrans = QualType(lhptee, 0), rtrans = QualType(rhptee, 0);
9132-
if (!S.Context.typesAreCompatible(ltrans, rtrans)) {
9133-
// Check if the pointee types are compatible ignoring the sign.
9134-
// We explicitly check for char so that we catch "char" vs
9135-
// "unsigned char" on systems where "char" is unsigned.
9136-
if (lhptee->isCharType())
9137-
ltrans = S.Context.UnsignedCharTy;
9138-
else if (lhptee->hasSignedIntegerRepresentation())
9139-
ltrans = S.Context.getCorrespondingUnsignedType(ltrans);
9140-
9141-
if (rhptee->isCharType())
9142-
rtrans = S.Context.UnsignedCharTy;
9143-
else if (rhptee->hasSignedIntegerRepresentation())
9144-
rtrans = S.Context.getCorrespondingUnsignedType(rtrans);
9145-
9146-
if (ltrans == rtrans) {
9147-
// Types are compatible ignoring the sign. Qualifier incompatibility
9148-
// takes priority over sign incompatibility because the sign
9149-
// warning can be disabled.
9150-
if (!S.IsAssignConvertCompatible(ConvTy))
9151-
return ConvTy;
9152-
9153-
return AssignConvertType::IncompatiblePointerSign;
9154-
}
9155-
9156-
// If we are a multi-level pointer, it's possible that our issue is simply
9157-
// one of qualification - e.g. char ** -> const char ** is not allowed. If
9158-
// the eventual target type is the same and the pointers have the same
9159-
// level of indirection, this must be the issue.
9160-
if (isa<PointerType>(lhptee) && isa<PointerType>(rhptee)) {
9161-
do {
9162-
std::tie(lhptee, lhq) =
9163-
cast<PointerType>(lhptee)->getPointeeType().split().asPair();
9164-
std::tie(rhptee, rhq) =
9165-
cast<PointerType>(rhptee)->getPointeeType().split().asPair();
9166-
9167-
// Inconsistent address spaces at this point is invalid, even if the
9168-
// address spaces would be compatible.
9169-
// FIXME: This doesn't catch address space mismatches for pointers of
9170-
// different nesting levels, like:
9171-
// __local int *** a;
9172-
// int ** b = a;
9173-
// It's not clear how to actually determine when such pointers are
9174-
// invalidly incompatible.
9175-
if (lhq.getAddressSpace() != rhq.getAddressSpace())
9176-
return AssignConvertType::
9177-
IncompatibleNestedPointerAddressSpaceMismatch;
9178-
9179-
} while (isa<PointerType>(lhptee) && isa<PointerType>(rhptee));
9180-
9181-
if (lhptee == rhptee)
9182-
return AssignConvertType::IncompatibleNestedPointerQualifiers;
9183-
}
9184-
9185-
// General pointer incompatibility takes priority over qualifiers.
9186-
if (RHSType->isFunctionPointerType() && LHSType->isFunctionPointerType())
9187-
return AssignConvertType::IncompatibleFunctionPointer;
9188-
return AssignConvertType::IncompatiblePointer;
9223+
QualType ltrans = QualType(lhptee, 0);
9224+
QualType rtrans;
9225+
std::optional<AssignConvertType> CvtT;
9226+
std::optional<QualType> ArrET = getMultiDimentionalArrayType(&S, OrigRHSType);
9227+
if (ArrET) {
9228+
rtrans = ArrET.value();
9229+
CvtT = checkPointerAssignmentCompatibility(
9230+
S, lhptee, rhptee, ltrans, rtrans, LHSType, RHSType, ConvTy);
9231+
if (CvtT && CvtT.value() != AssignConvertType::IncompatiblePointer)
9232+
return CvtT.value();
9233+
}
9234+
if (!ArrET ||
9235+
(CvtT && CvtT.value() == AssignConvertType::IncompatiblePointer)) {
9236+
rtrans = QualType(rhptee, 0);
9237+
CvtT = checkPointerAssignmentCompatibility(
9238+
S, lhptee, rhptee, ltrans, rtrans, LHSType, RHSType, ConvTy);
9239+
if (CvtT)
9240+
return CvtT.value();
91899241
}
9242+
91909243
bool DiscardingCFIUncheckedCallee, AddingCFIUncheckedCallee;
91919244
if (!S.getLangOpts().CPlusPlus &&
91929245
S.IsFunctionConversion(ltrans, rtrans, &DiscardingCFIUncheckedCallee,
@@ -9313,6 +9366,22 @@ static bool isVector(QualType QT, QualType ElementType) {
93139366
return false;
93149367
}
93159368

9369+
/// This helper function returns pre array-to-pointer decay type if possible
9370+
static QualType getPreDecayType(ExprResult &RHS) {
9371+
QualType OrigRHSType = RHS.get()->getType();
9372+
ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(RHS.get());
9373+
if (ICE) {
9374+
Expr *E = ICE->getSubExpr();
9375+
if (E) {
9376+
DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E);
9377+
if (DRE) {
9378+
OrigRHSType = DRE->getType();
9379+
}
9380+
}
9381+
}
9382+
return OrigRHSType;
9383+
}
9384+
93169385
/// CheckAssignmentConstraints (C99 6.5.16) - This routine currently
93179386
/// has code to accommodate several GCC extensions when type checking
93189387
/// pointers. Here are some objectionable examples that GCC considers warnings:
@@ -9510,7 +9579,9 @@ AssignConvertType Sema::CheckAssignmentConstraints(QualType LHSType,
95109579
Kind = CK_NoOp;
95119580
else
95129581
Kind = CK_BitCast;
9513-
return checkPointerTypesForAssignment(*this, LHSType, RHSType,
9582+
9583+
QualType OrigType = getPreDecayType(RHS);
9584+
return checkPointerTypesForAssignment(*this, LHSType, RHSType, OrigType,
95149585
RHS.get()->getBeginLoc());
95159586
}
95169587

@@ -9629,7 +9700,6 @@ AssignConvertType Sema::CheckAssignmentConstraints(QualType LHSType,
96299700
Context.getObjCClassRedefinitionType())) {
96309701
return AssignConvertType::Compatible;
96319702
}
9632-
96339703
return AssignConvertType::IncompatiblePointer;
96349704
}
96359705

@@ -9902,7 +9972,7 @@ AssignConvertType Sema::CheckSingleAssignmentConstraints(QualType LHSType,
99029972
RHS.get()->getType().getCanonicalType().getUnqualifiedType();
99039973
QualType CanLHS = LHSType.getCanonicalType().getUnqualifiedType();
99049974
if (CanRHS->isVoidPointerType() && CanLHS->isPointerType()) {
9905-
Ret = checkPointerTypesForAssignment(*this, CanLHS, CanRHS,
9975+
Ret = checkPointerTypesForAssignment(*this, CanLHS, CanRHS, CanRHS,
99069976
RHS.get()->getExprLoc());
99079977
// Anything that's not considered perfectly compatible would be
99089978
// incompatible in C++.

clang/test/Sema/multi-dim-array.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// RUN: %clang_cc1 %s -fsyntax-only -verify -verify=c2x -pedantic -Wno-strict-prototypes -Wno-zero-length-array
2+
3+
int array_acceptor_good(unsigned long * par1)
4+
{
5+
return par1 != (unsigned long *)0;
6+
}
7+
8+
int array_acceptor_bad(unsigned long * par1) // expected-note {{passing argument to parameter 'par1' here}}
9+
{
10+
return par1 != (unsigned long *)0;
11+
}
12+
13+
int array_acceptor_bad2(unsigned long * par1) // expected-note {{passing argument to parameter 'par1' here}}
14+
{
15+
return par1 != (unsigned long *)0;
16+
}
17+
18+
struct S
19+
{
20+
int a;
21+
};
22+
23+
int array_acceptor_bad1(struct S * par1) // expected-note {{passing argument to parameter 'par1' here}}
24+
{
25+
return par1 != (struct S *)0;
26+
}
27+
28+
int array_struct_acceptor(struct S * par1)
29+
{
30+
return par1 != (struct S *)0;
31+
}
32+
33+
34+
int array_tester()
35+
{
36+
unsigned long mdarr[5][6];
37+
double mddarr[5][6];
38+
unsigned long sdarr[30];
39+
unsigned long mdarr3d[5][6][2];
40+
unsigned long mdarr4d[5][6][2][1];
41+
unsigned long mdarrz4d[5][6][0][1];
42+
struct S mdsarr[5][6][2];
43+
44+
array_acceptor_good(sdarr);
45+
array_acceptor_good(mdarr);
46+
array_acceptor_good(mdarr3d);
47+
array_acceptor_good(mdarr4d);
48+
array_acceptor_bad(mddarr); // expected-error {{incompatible pointer types passing 'double[5][6]' to parameter of type 'unsigned long *'}}
49+
array_acceptor_bad1(mddarr); // expected-error {{incompatible pointer types passing 'double[5][6]' to parameter of type 'struct S *'}}
50+
array_acceptor_bad2(mdarrz4d); // expected-error {{incompatible pointer types passing 'unsigned long[5][6][0][1]' to parameter of type 'unsigned long *'}}
51+
array_struct_acceptor(mdsarr);
52+
}

0 commit comments

Comments
 (0)