diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -8831,6 +8831,14 @@ def warn_array_index_exceeds_bounds : Warning< "array index %0 is past the end of the array (which contains %1 " "element%s2)">, InGroup; +def warn_ptr_arith_exceeds_max_addressable_bounds : Warning< + "the pointer incremented by %0 refers past the last possible element for an array in %1-bit " + "address space containing %2-bit (%3-byte) elements (max possible %4 element%s5)">, + InGroup; +def warn_array_index_exceeds_max_addressable_bounds : Warning< + "array index %0 refers past the last possible element for an array in %1-bit " + "address space containing %2-bit (%3-byte) elements (max possible %4 element%s5)">, + InGroup; def note_array_declared_here : Note< "array %0 declared here">; diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -13941,11 +13941,11 @@ const ConstantArrayType *ArrayTy = Context.getAsConstantArrayType(BaseExpr->getType()); - if (!ArrayTy) - return; - - const Type *BaseType = ArrayTy->getElementType().getTypePtr(); - if (EffectiveType->isDependentType() || BaseType->isDependentType()) + const Type *BaseType = + ArrayTy == nullptr ? nullptr : ArrayTy->getElementType().getTypePtr(); + bool isUnboundedArray = (BaseType == nullptr); + if (EffectiveType->isDependentType() || + (!isUnboundedArray && BaseType->isDependentType())) return; Expr::EvalResult Result; @@ -13963,77 +13963,132 @@ ND = ME->getMemberDecl(); if (index.isUnsigned() || !index.isNegative()) { - // It is possible that the type of the base expression after - // IgnoreParenCasts is incomplete, even though the type of the base - // expression before IgnoreParenCasts is complete (see PR39746 for an - // example). In this case we have no information about whether the array - // access exceeds the array bounds. However we can still diagnose an array - // access which precedes the array bounds. - if (BaseType->isIncompleteType()) - return; + if (isUnboundedArray) { + const auto &ASTC = getASTContext(); + unsigned AddrBits = + ASTC.getTargetInfo().getPointerWidth(ASTC.getTargetAddressSpace( + EffectiveType->getCanonicalTypeInternal())); + if (index.getBitWidth() < AddrBits) + index = index.zext(AddrBits); + CharUnits ElemBytes = ASTC.getTypeSizeInChars(EffectiveType); + llvm::APInt apElemBytes(index.getBitWidth(), ElemBytes.getQuantity()); + // If index has more active bits than address space, we already know + // we have a bounds violation to warn about. Otherwise, compute + // address of (index + 1)th element, and warn about bounds violation + // only if that address exceeds address space. + if (index.getActiveBits() <= AddrBits) { + bool overflow; + llvm::APInt product(index); + product += 1; + product = product.umul_ov(apElemBytes, overflow); + if (!overflow && product.getActiveBits() <= AddrBits) + return; + } - llvm::APInt size = ArrayTy->getSize(); - if (!size.isStrictlyPositive()) - return; + // Need to compute max possible elements in address space, since that + // is included in diag message. + llvm::APInt MaxElems = llvm::APInt::getMaxValue(AddrBits); + MaxElems = + MaxElems.zext(std::max(AddrBits << 1, apElemBytes.getBitWidth())); + MaxElems += 1; + if (MaxElems.getBitWidth() < apElemBytes.getBitWidth()) + MaxElems = MaxElems.zext(apElemBytes.getBitWidth()); + else if (apElemBytes.getBitWidth() < MaxElems.getBitWidth()) + apElemBytes = apElemBytes.zext(MaxElems.getBitWidth()); + MaxElems = MaxElems.udiv(apElemBytes); + + unsigned DiagID = diag::warn_ptr_arith_exceeds_max_addressable_bounds; + if (ASE) + DiagID = diag::warn_array_index_exceeds_max_addressable_bounds; + + // Diag message shows element size in bits and in "bytes" (platform- + // dependent CharUnits) + DiagRuntimeBehavior(BaseExpr->getBeginLoc(), BaseExpr, + PDiag(DiagID) + << index.toString(10, true) << AddrBits + << (unsigned)ASTC.toBits(ElemBytes) + << apElemBytes.toString(10, false) + << MaxElems.toString(10, false) + << (unsigned)MaxElems.getLimitedValue(~0U) + << IndexExpr->getSourceRange()); + } else { + // It is possible that the type of the base expression after + // IgnoreParenCasts is incomplete, even though the type of the base + // expression before IgnoreParenCasts is complete (see PR39746 for an + // example). In this case we have no information about whether the array + // access exceeds the array bounds. However we can still diagnose an array + // access which precedes the array bounds. + if (BaseType->isIncompleteType()) + return; + + llvm::APInt size = ArrayTy->getSize(); + if (!size.isStrictlyPositive()) + return; - if (BaseType != EffectiveType) { - // Make sure we're comparing apples to apples when comparing index to size - uint64_t ptrarith_typesize = Context.getTypeSize(EffectiveType); - uint64_t array_typesize = Context.getTypeSize(BaseType); - // Handle ptrarith_typesize being zero, such as when casting to void* - if (!ptrarith_typesize) ptrarith_typesize = 1; - if (ptrarith_typesize != array_typesize) { - // There's a cast to a different size type involved - uint64_t ratio = array_typesize / ptrarith_typesize; - // TODO: Be smarter about handling cases where array_typesize is not a - // multiple of ptrarith_typesize - if (ptrarith_typesize * ratio == array_typesize) - size *= llvm::APInt(size.getBitWidth(), ratio); + if (BaseType != EffectiveType) { + // Make sure we're comparing apples to apples when comparing index to + // size + uint64_t ptrarith_typesize = Context.getTypeSize(EffectiveType); + uint64_t array_typesize = Context.getTypeSize(BaseType); + // Handle ptrarith_typesize being zero, such as when casting to void* + if (!ptrarith_typesize) + ptrarith_typesize = 1; + if (ptrarith_typesize != array_typesize) { + // There's a cast to a different size type involved + uint64_t ratio = array_typesize / ptrarith_typesize; + // TODO: Be smarter about handling cases where array_typesize is not a + // multiple of ptrarith_typesize + if (ptrarith_typesize * ratio == array_typesize) + size *= llvm::APInt(size.getBitWidth(), ratio); + } } - } - if (size.getBitWidth() > index.getBitWidth()) - index = index.zext(size.getBitWidth()); - else if (size.getBitWidth() < index.getBitWidth()) - size = size.zext(index.getBitWidth()); + if (size.getBitWidth() > index.getBitWidth()) + index = index.zext(size.getBitWidth()); + else if (size.getBitWidth() < index.getBitWidth()) + size = size.zext(index.getBitWidth()); - // For array subscripting the index must be less than size, but for pointer - // arithmetic also allow the index (offset) to be equal to size since - // computing the next address after the end of the array is legal and - // commonly done e.g. in C++ iterators and range-based for loops. - if (AllowOnePastEnd ? index.ule(size) : index.ult(size)) - return; + // For array subscripting the index must be less than size, but for + // pointer arithmetic also allow the index (offset) to be equal to size + // since computing the next address after the end of the array is legal + // and commonly done e.g. in C++ iterators and range-based for loops. + if (AllowOnePastEnd ? index.ule(size) : index.ult(size)) + return; - // Also don't warn for arrays of size 1 which are members of some - // structure. These are often used to approximate flexible arrays in C89 - // code. - if (IsTailPaddedMemberArray(*this, size, ND)) - return; + // Also don't warn for arrays of size 1 which are members of some + // structure. These are often used to approximate flexible arrays in C89 + // code. + if (IsTailPaddedMemberArray(*this, size, ND)) + return; - // Suppress the warning if the subscript expression (as identified by the - // ']' location) and the index expression are both from macro expansions - // within a system header. - if (ASE) { - SourceLocation RBracketLoc = SourceMgr.getSpellingLoc( - ASE->getRBracketLoc()); - if (SourceMgr.isInSystemHeader(RBracketLoc)) { - SourceLocation IndexLoc = - SourceMgr.getSpellingLoc(IndexExpr->getBeginLoc()); - if (SourceMgr.isWrittenInSameFile(RBracketLoc, IndexLoc)) - return; + // Suppress the warning if the subscript expression (as identified by the + // ']' location) and the index expression are both from macro expansions + // within a system header. + if (ASE) { + SourceLocation RBracketLoc = + SourceMgr.getSpellingLoc(ASE->getRBracketLoc()); + if (SourceMgr.isInSystemHeader(RBracketLoc)) { + SourceLocation IndexLoc = + SourceMgr.getSpellingLoc(IndexExpr->getBeginLoc()); + if (SourceMgr.isWrittenInSameFile(RBracketLoc, IndexLoc)) + return; + } } - } - unsigned DiagID = diag::warn_ptr_arith_exceeds_bounds; - if (ASE) - DiagID = diag::warn_array_index_exceeds_bounds; + unsigned DiagID = diag::warn_ptr_arith_exceeds_bounds; + if (ASE) + DiagID = diag::warn_array_index_exceeds_bounds; - DiagRuntimeBehavior(BaseExpr->getBeginLoc(), BaseExpr, - PDiag(DiagID) << index.toString(10, true) - << size.toString(10, true) - << (unsigned)size.getLimitedValue(~0U) - << IndexExpr->getSourceRange()); + DiagRuntimeBehavior(BaseExpr->getBeginLoc(), BaseExpr, + PDiag(DiagID) << index.toString(10, true) + << size.toString(10, true) + << (unsigned)size.getLimitedValue(~0U) + << IndexExpr->getSourceRange()); + } } else { + if (isUnboundedArray) + return; + unsigned DiagID = diag::warn_array_index_precedes_bounds; if (!ASE) { DiagID = diag::warn_ptr_arith_precedes_bounds; diff --git a/clang/test/Sema/const-eval.c b/clang/test/Sema/const-eval.c --- a/clang/test/Sema/const-eval.c +++ b/clang/test/Sema/const-eval.c @@ -140,10 +140,10 @@ // We evaluate these by providing 2s' complement semantics in constant // expressions, like we do for integers. -void *PR28739a = (__int128)(unsigned long)-1 + &PR28739a; -void *PR28739b = &PR28739b + (__int128)(unsigned long)-1; -__int128 PR28739c = (&PR28739c + (__int128)(unsigned long)-1) - &PR28739c; -void *PR28739d = &(&PR28739d)[(__int128)(unsigned long)-1]; +void *PR28739a = (__int128)(unsigned long)-1 + &PR28739a; // expected-warning {{refers past the last possible element}} +void *PR28739b = &PR28739b + (__int128)(unsigned long)-1; // expected-warning {{refers past the last possible element}} +__int128 PR28739c = (&PR28739c + (__int128)(unsigned long)-1) - &PR28739c; // expected-warning {{refers past the last possible element}} +void *PR28739d = &(&PR28739d)[(__int128)(unsigned long)-1]; // expected-warning {{refers past the last possible element}} struct PR35214_X { int k; diff --git a/clang/test/Sema/unbounded-array-bounds.c b/clang/test/Sema/unbounded-array-bounds.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/unbounded-array-bounds.c @@ -0,0 +1,70 @@ +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fsyntax-only %s 2>&1 | FileCheck --check-prefix=CHECK-X86-ADDR64 %s \ +// RUN: --implicit-check-not 'past the last possible element' +// RUN: %clang_cc1 -triple i386-pc-linux-gnu -fsyntax-only %s 2>&1 | FileCheck --check-prefix=CHECK-I386-ADDR32 %s \ +// RUN: --implicit-check-not 'past the last possible element' +// RUN: %clang_cc1 -triple avr-pc-linux-gnu -fsyntax-only %s 2>&1 | FileCheck --check-prefix=CHECK-AVR-ADDR16 %s \ +// RUN: --implicit-check-not 'past the last possible element' + +struct S { + long long a; + char b; + long long c; + short d; +}; + +struct S s[]; + +void f1() { + ++s[3].a; + ++s[7073650413200313099].b; + // CHECK-X86-ADDR64: :[[@LINE-1]]:5: warning: {{.*}} past the last possible element {{.*}} in 64-bit {{.*}} (max possible 576460752303423488 elements) + // CHECK-I386-ADDR32: :[[@LINE-2]]:5: warning: {{.*}} past the last possible element {{.*}} in 32-bit {{.*}} (max possible 178956970 elements) + // CHECK-AVR-ADDR16: :[[@LINE-3]]:5: warning: {{.*}} past the last possible element {{.*}} in 16-bit {{.*}} (max possible 3276 elements) + ++s[7073650].c; + // CHECK-AVR-ADDR16: :[[@LINE-1]]:5: warning: {{.*}} past the last possible element {{.*}} in 16-bit {{.*}} (max possible 3276 elements) +} + +long long ll[]; + +void f2() { + ++ll[3]; + ++ll[2705843009213693952]; + // CHECK-X86-ADDR64: :[[@LINE-1]]:5: warning: {{.*}} past the last possible element {{.*}} in 64-bit {{.*}} (max possible 2305843009213693952 elements) + // CHECK-I386-ADDR32: :[[@LINE-2]]:5: warning: {{.*}} past the last possible element {{.*}} in 32-bit {{.*}} (max possible 536870912 elements) + // CHECK-AVR-ADDR16: :[[@LINE-3]]:5: warning: {{.*}} past the last possible element {{.*}} in 16-bit {{.*}} (max possible 8192 elements) + ++ll[847073650]; + // CHECK-I386-ADDR32: :[[@LINE-1]]:5: warning: {{.*}} past the last possible element {{.*}} in 32-bit {{.*}} (max possible 536870912 elements) + // CHECK-AVR-ADDR16: :[[@LINE-2]]:5: warning: {{.*}} past the last possible element {{.*}} in 16-bit {{.*}} (max possible 8192 elements) +} + +void f3(struct S p[]) { + ++p[3].a; + ++p[7073650413200313099].b; + // CHECK-X86-ADDR64: :[[@LINE-1]]:5: warning: {{.*}} past the last possible element {{.*}} in 64-bit {{.*}} (max possible 576460752303423488 elements) + // CHECK-I386-ADDR32: :[[@LINE-2]]:5: warning: {{.*}} past the last possible element {{.*}} in 32-bit {{.*}} (max possible 178956970 elements) + // CHECK-AVR-ADDR16: :[[@LINE-3]]:5: warning: {{.*}} past the last possible element {{.*}} in 16-bit {{.*}} (max possible 3276 elements) + ++p[7073650].c; + // CHECK-AVR-ADDR16: :[[@LINE-1]]:5: warning: {{.*}} past the last possible element {{.*}} in 16-bit {{.*}} (max possible 3276 elements) +} + +void f4(struct S *p) { + p += 3; + p += 7073650413200313099; + // CHECK-X86-ADDR64: :[[@LINE-1]]:3: warning: {{.*}} past the last possible element {{.*}} in 64-bit {{.*}} (max possible 576460752303423488 elements) + // CHECK-I386-ADDR32: :[[@LINE-2]]:3: warning: {{.*}} past the last possible element {{.*}} in 32-bit {{.*}} (max possible 178956970 elements) + // CHECK-AVR-ADDR16: :[[@LINE-3]]:3: warning: {{.*}} past the last possible element {{.*}} in 16-bit {{.*}} (max possible 3276 elements) + p += 7073650; + // CHECK-AVR-ADDR16: :[[@LINE-1]]:3: warning: {{.*}} past the last possible element {{.*}} in 16-bit {{.*}} (max possible 3276 elements) +} + +struct BQ { + struct S bigblock[3276]; +}; + +struct BQ bq[]; + +void f5() { + ++bq[0].bigblock[0].a; + ++bq[1].bigblock[0].a; + // CHECK-AVR-ADDR16: :[[@LINE-1]]:5: warning: {{.*}} past the last possible element {{.*}} in 16-bit {{.*}} (max possible 1 element) +} diff --git a/clang/test/SemaCXX/constant-expression-cxx1y.cpp b/clang/test/SemaCXX/constant-expression-cxx1y.cpp --- a/clang/test/SemaCXX/constant-expression-cxx1y.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx1y.cpp @@ -1018,8 +1018,9 @@ } constexpr void PR28739(int n) { // expected-error {{never produces a constant}} - int *p = &n; + int *p = &n; // expected-note {{declared here}} p += (__int128)(unsigned long)-1; // expected-note {{cannot refer to element 18446744073709551615 of non-array object in a constant expression}} + // expected-warning@-1 {{refers past the last possible element}} } constexpr void Void(int n) {