llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Timm Baeder (tbaederr) <details> <summary>Changes</summary> Which can be used to implement strlen-like functionality. --- Full diff: https://github.com/llvm/llvm-project/pull/218601.diff 5 Files Affected: - (modified) clang/include/clang/AST/Expr.h (+9) - (modified) clang/lib/AST/Expr.cpp (+22) - (modified) clang/lib/AST/ExprConstant.cpp (+10-16) - (modified) clang/unittests/AST/CMakeLists.txt (+1) - (added) clang/unittests/AST/StringLiteral.cpp (+78) ``````````diff diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 72762c668f26a..2a88b0e0d663c 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -1925,6 +1925,15 @@ class StringLiteral final return V; } + /// Scan the string literal contents for a code unit with value 0. + /// If \p StartIndex is outside of the length of the string, this returns \c + /// std::nullopt. + /// + /// Otherwise, returns the offset (in code units, not bytes) of the zero code + /// unit, starting at index \p StartIndex. If no such code unit could be + /// found, this returns `getLength() - StartIndex`. + UnsignedOrNone findZeroCodeUnit(unsigned StartIndex = 0) const; + unsigned getByteLength() const { return getCharByteWidth() * getLength(); } unsigned getLength() const { return *getTrailingObjects<unsigned>(); } unsigned getCharByteWidth() const { return StringLiteralBits.CharByteWidth; } diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index e501527ed9b04..6ce0a29aa3bd7 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -1407,6 +1407,28 @@ StringLiteral::getLocationOfByte(unsigned ByteNo, const SourceManager &SM, } } +UnsignedOrNone StringLiteral::findZeroCodeUnit(unsigned StartIndex) const { + unsigned Length = getLength(); + if (StartIndex > Length) + return std::nullopt; + + if (getCharByteWidth() == 1) { + StringRef::size_type Pos = getString().substr(StartIndex).find('\0'); + if (Pos == StringRef::npos) + return Length - StartIndex; + return Pos; + } + + unsigned Result = 0; + for (unsigned I = StartIndex; I != Length; ++I) { + if (getCodeUnit(I) == 0) + break; + ++Result; + } + + return Result; +} + /// getOpcodeStr - Turn an Opcode enum value into the punctuation char it /// corresponds to, e.g. "sizeof" or "[pre]++". StringRef UnaryOperator::getOpcodeStr(Opcode Op) { diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index d55749100658f..23b80e1dc6bec 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -23119,31 +23119,25 @@ EvaluateBuiltinStrLen(const Expr *E, EvalInfo &Info, if (!EvaluatePointer(E, String, Info)) return std::nullopt; - QualType CharTy = E->getType()->getPointeeType(); - // Fast path: if it's a string literal, search the string value. if (const StringLiteral *S = dyn_cast_or_null<StringLiteral>( String.getLValueBase().dyn_cast<const Expr *>())) { StringRef Str = S->getBytes(); int64_t Off = String.Offset.getQuantity(); - if (Off >= 0 && (uint64_t)Off <= (uint64_t)Str.size() && - S->getCharByteWidth() == 1 && - // FIXME: Add fast-path for wchar_t too. - Info.Ctx.hasSameUnqualifiedType(CharTy, Info.Ctx.CharTy)) { - Str = Str.substr(Off); - - StringRef::size_type Pos = Str.find(0); - if (Pos != StringRef::npos) - Str = Str.substr(0, Pos); - - if (StringResult) + if (Off >= 0 && (uint64_t)Off <= (uint64_t)Str.size()) { + UnsignedOrNone ZeroIndex = S->findZeroCodeUnit(Off); + if (StringResult) { + if (ZeroIndex) + Str = Str.substr(Off, *ZeroIndex); *StringResult = Str; - return Str.size(); - } + } - // Fall through to slow path. + return ZeroIndex.value_or(Str.size()); + } + // For an invalid index, fall through to the offset handling below. } + QualType CharTy = E->getType()->getPointeeType(); // Slow path: scan the bytes of the string looking for the terminating 0. for (uint64_t Strlen = 0; /**/; ++Strlen) { APValue Char; diff --git a/clang/unittests/AST/CMakeLists.txt b/clang/unittests/AST/CMakeLists.txt index 81010e0469685..e726cc3299a5a 100644 --- a/clang/unittests/AST/CMakeLists.txt +++ b/clang/unittests/AST/CMakeLists.txt @@ -3,6 +3,7 @@ add_subdirectory(ByteCode) add_clang_unittest(ASTTests ASTContextParentMapTest.cpp ASTDumperTest.cpp + StringLiteral.cpp ASTExprTest.cpp ASTImporterFixtures.cpp ASTImporterTest.cpp diff --git a/clang/unittests/AST/StringLiteral.cpp b/clang/unittests/AST/StringLiteral.cpp new file mode 100644 index 0000000000000..1cd56d1725e35 --- /dev/null +++ b/clang/unittests/AST/StringLiteral.cpp @@ -0,0 +1,78 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Mangle.h" +#include "clang/AST/TypeBase.h" +#include "clang/Basic/LLVM.h" +#include "clang/Tooling/Tooling.h" +#include "gtest/gtest.h" +#include <cassert> +#include <memory> +#include <string> + +using namespace clang::tooling; +using namespace clang; + +static void ConvertUTF8ToWideString(unsigned CharByteWidth, StringRef Source, + SmallString<32> &Target) { + Target.resize(CharByteWidth * (Source.size() + 1)); + char *ResultPtr = &Target[0]; + const llvm::UTF8 *ErrorPtr; + bool success = + llvm::ConvertUTF8toWide(CharByteWidth, Source, ResultPtr, ErrorPtr); + (void)success; + assert(success); + Target.resize(ResultPtr - &Target[0]); +} + +TEST(StringLiteral, findZeroCodeUnit) { + auto AST = tooling::buildASTFromCodeWithArgs("", {}); + ASTContext &Ctx = AST->getASTContext(); + + auto getCharArrayType = [&Ctx](unsigned Size) -> QualType { + return Ctx.getStringLiteralArrayType(Ctx.CharTy.withConst(), Size); + }; + auto getWCharArrayType = [&Ctx](unsigned Size) -> QualType { + return Ctx.getStringLiteralArrayType(Ctx.WCharTy.withConst(), Size); + }; + + const auto *S1 = + StringLiteral::Create(Ctx, "abcdef", StringLiteralKind::Ordinary, false, + getCharArrayType(7), {}); + ASSERT_EQ(S1->getLength(), 6u); + ASSERT_EQ(*S1->findZeroCodeUnit(), 6u); + ASSERT_EQ(*S1->findZeroCodeUnit(4), 2u); + ASSERT_FALSE(S1->findZeroCodeUnit(16).has_value()); + + const auto *S2 = StringLiteral::Create(Ctx, StringRef("a\0bcd", 6), + StringLiteralKind::Ordinary, false, + getCharArrayType(6), {}); + ASSERT_EQ(S2->getLength(), 6u); + ASSERT_EQ(*S2->findZeroCodeUnit(), 1u); + ASSERT_EQ(*S2->findZeroCodeUnit(1), 0u); + ASSERT_EQ(*S2->findZeroCodeUnit(2), 3u); + + SmallString<32> RawChars; + ConvertUTF8ToWideString(4, "abcdef", RawChars); + const auto *S3 = StringLiteral::Create( + Ctx, RawChars, StringLiteralKind::UTF32, false, getWCharArrayType(7), {}); + ASSERT_EQ(S3->getLength(), 6u); + ASSERT_EQ(*S3->findZeroCodeUnit(), 6u); + ASSERT_EQ(*S3->findZeroCodeUnit(2), 4u); + + ConvertUTF8ToWideString(4, StringRef("abc\0ef", 6), RawChars); + const auto *S4 = StringLiteral::Create( + Ctx, RawChars, StringLiteralKind::UTF32, false, getWCharArrayType(7), {}); + ASSERT_EQ(S4->getLength(), 6u); + ASSERT_EQ(S4->findZeroCodeUnit(), 3u); + ASSERT_EQ(S4->findZeroCodeUnit(3u), 0u); + ASSERT_EQ(S4->findZeroCodeUnit(4u), 2u); +} `````````` </details> https://github.com/llvm/llvm-project/pull/218601 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
