Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ci/conda_env_gandiva.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@
# under the License.

clang
llvmdev<23
llvmdev<24
1 change: 1 addition & 0 deletions cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ set(ARROW_DOC_DIR "${CMAKE_INSTALL_DOCDIR}")
set(BUILD_SUPPORT_DIR "${CMAKE_SOURCE_DIR}/build-support")

set(ARROW_LLVM_VERSIONS
"23.1"
"22.1"
"21.1"
"20.1"
Expand Down
10 changes: 0 additions & 10 deletions cpp/src/arrow/compute/exec.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1185,16 +1185,6 @@ class ScalarAggExecutor : public KernelExecutorImpl<ScalarAggregateKernel> {
const FunctionOptions* options_;
};

template <typename ExecutorType,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a template function which doesn't have any callers.

typename FunctionType = typename ExecutorType::FunctionType>
Result<std::unique_ptr<KernelExecutor>> MakeExecutor(ExecContext* ctx,
const Function* func,
const FunctionOptions* options) {
DCHECK_EQ(ExecutorType::function_kind, func->kind());
auto typed_func = checked_cast<const FunctionType*>(func);
return std::make_unique<ExecutorType>(ctx, typed_func, options);
}

} // namespace

Status PropagateNulls(KernelContext* ctx, const ExecSpan& batch, ArrayData* output) {
Expand Down
4 changes: 2 additions & 2 deletions cpp/src/arrow/compute/function_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -535,8 +535,8 @@ static inline std::enable_if_t<is_optional_v<T>, Result<T>> GenericFromScalar(
}

template <typename T>
static enable_if_same<typename CTypeTraits<T>::ArrowType, ListType, Result<T>>
GenericFromScalar(const std::shared_ptr<Scalar>& value) {
enable_if_same<typename CTypeTraits<T>::ArrowType, ListType, Result<T>> GenericFromScalar(
const std::shared_ptr<Scalar>& value) {
using ValueType = typename T::value_type;
if (value->type->id() != Type::LIST) {
return Status::Invalid("Expected type LIST but got ", value->type->ToString());
Expand Down
14 changes: 7 additions & 7 deletions cpp/src/arrow/compute/kernels/codegen_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -481,8 +481,8 @@ struct UnboxScalar<Decimal256Type> {

template <typename T, typename VisitFunc, typename NullFunc>
requires std::is_void_v<std::invoke_result_t<VisitFunc, typename GetViewType<T>::T>>
static void VisitArrayValuesInline(const ArraySpan& arr, VisitFunc&& valid_func,
NullFunc&& null_func) {
void VisitArrayValuesInline(const ArraySpan& arr, VisitFunc&& valid_func,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LLVM 23 added -Wunused-template to -Wall.
https://releases.llvm.org/23.1.0/tools/clang/docs/ReleaseNotes.html

-Wunused-template is now part of -Wunused (which is enabled by -Wall). It diagnoses unused function and variable templates with internal linkage, which in a header is a latent ODR hazard. It can be disabled with -Wno-unused-template. (#202945)

I don't think there's any harm in removing static. Template functions are inherently similar to inline functions, and ODR issues won't arise.

NullFunc&& null_func) {
VisitArraySpanInline<T>(
arr,
[&](typename GetViewType<T>::PhysicalType v) {
Expand All @@ -494,8 +494,8 @@ static void VisitArrayValuesInline(const ArraySpan& arr, VisitFunc&& valid_func,
template <typename T, typename VisitFunc, typename NullFunc>
requires std::is_same_v<std::invoke_result_t<VisitFunc, typename GetViewType<T>::T>,
Status>
static Status VisitArrayValuesInline(const ArraySpan& arr, VisitFunc&& valid_func,
NullFunc&& null_func) {
Status VisitArrayValuesInline(const ArraySpan& arr, VisitFunc&& valid_func,
NullFunc&& null_func) {
return VisitArraySpanInline<T>(
arr,
[&](typename GetViewType<T>::PhysicalType v) {
Expand All @@ -507,8 +507,8 @@ static Status VisitArrayValuesInline(const ArraySpan& arr, VisitFunc&& valid_fun
// Like VisitArrayValuesInline, but for binary functions.

template <typename Arg0Type, typename Arg1Type, typename VisitFunc, typename NullFunc>
static void VisitTwoArrayValuesInline(const ArraySpan& arr0, const ArraySpan& arr1,
VisitFunc&& valid_func, NullFunc&& null_func) {
void VisitTwoArrayValuesInline(const ArraySpan& arr0, const ArraySpan& arr1,
VisitFunc&& valid_func, NullFunc&& null_func) {
ArrayIterator<Arg0Type> arr0_it(arr0);
ArrayIterator<Arg1Type> arr1_it(arr1);

Expand Down Expand Up @@ -584,7 +584,7 @@ namespace applicator {
// static Status Call(KernelContext*, const Scalar& arg0, const ArraySpan& arg1,
// ExecResult* out)
template <typename Operator>
static Status SimpleBinary(KernelContext* ctx, const ExecSpan& batch, ExecResult* out) {
Status SimpleBinary(KernelContext* ctx, const ExecSpan& batch, ExecResult* out) {
if (batch.length == 0) return Status::OK();

if (batch[0].is_array()) {
Expand Down
2 changes: 1 addition & 1 deletion cpp/src/arrow/compute/kernels/vector_hash.cc
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ class ValueCountsAction final : ActionBase {
}

template <class Index>
void ObserveNullNotFound(Index index) {
[[maybe_unused]] void ObserveNullNotFound(Index index) {
ARROW_LOG(FATAL) << "ObserveNullNotFound without err_status should not be called";
}

Expand Down
20 changes: 13 additions & 7 deletions cpp/src/arrow/scalar.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include "arrow/scalar.h"

#include <chrono>
#include <memory>
#include <ostream>
#include <sstream>
Expand All @@ -31,6 +32,7 @@
#include "arrow/type.h"
#include "arrow/util/bitmap_ops.h"
#include "arrow/util/checked_cast.h"
#include "arrow/util/chrono_internal.h"
#include "arrow/util/decimal.h"
#include "arrow/util/formatting.h"
#include "arrow/util/hashing.h"
Expand Down Expand Up @@ -1191,41 +1193,45 @@ constexpr int64_t kMillisecondsInDay = 86400000;

// date to date
template <typename To>
enable_if_t<std::is_same<To, Date64Scalar>::value, Result<std::shared_ptr<Scalar>>>
enable_if_t<std::is_same<To, Date64Type>::value, Result<std::shared_ptr<Scalar>>>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is another issue exposed by -Wunused-template. The template instantiation type was incorrect here, causing the function that was supposed to execute this path to fall through to the "NotImplemented" exception instead. A unit test has been added.

CastImpl(const Date32Scalar& from, std::shared_ptr<DataType> to_type) {
return std::make_shared<Date64Scalar>(from.value * kMillisecondsInDay,
std::move(to_type));
}
template <typename To>
enable_if_t<std::is_same<To, Date32Scalar>::value, Result<std::shared_ptr<Scalar>>>
enable_if_t<std::is_same<To, Date32Type>::value, Result<std::shared_ptr<Scalar>>>
CastImpl(const Date64Scalar& from, std::shared_ptr<DataType> to_type) {
return std::make_shared<Date32Scalar>(
static_cast<int32_t>(from.value / kMillisecondsInDay), std::move(to_type));
}

// timestamp to date
template <typename To>
enable_if_t<std::is_same<To, Date64Scalar>::value, Result<std::shared_ptr<Scalar>>>
enable_if_t<std::is_same<To, Date64Type>::value, Result<std::shared_ptr<Scalar>>>
CastImpl(const TimestampScalar& from, std::shared_ptr<DataType> to_type) {
ARROW_ASSIGN_OR_RAISE(
auto millis,
util::ConvertTimestampValue(from.type, timestamp(TimeUnit::MILLI), from.value));
return std::make_shared<Date64Scalar>(millis - millis % kMillisecondsInDay,
const auto days_since_epoch =
internal::chrono::floor<internal::chrono::days>(std::chrono::milliseconds{millis});
return std::make_shared<Date64Scalar>(days_since_epoch.count() * kMillisecondsInDay,
Comment on lines +1215 to +1217

@HuaHuaY HuaHuaY Sep 15, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CastTo returns a Result

does not imply that bounds checking is required. Other conversions do not include such checks, so I am inclined not to make any changes. If someone encounters a relevant use case, they can open a PR to modify it then.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed on skipping bounds checks here. If a real use case/issue shows up, we can follow up it separately

std::move(to_type));
}
template <typename To>
enable_if_t<std::is_same<To, Date32Scalar>::value, Result<std::shared_ptr<Scalar>>>
enable_if_t<std::is_same<To, Date32Type>::value, Result<std::shared_ptr<Scalar>>>
CastImpl(const TimestampScalar& from, std::shared_ptr<DataType> to_type) {
ARROW_ASSIGN_OR_RAISE(
auto millis,
util::ConvertTimestampValue(from.type, timestamp(TimeUnit::MILLI), from.value));
return std::make_shared<Date32Scalar>(static_cast<int32_t>(millis / kMillisecondsInDay),
const auto days_since_epoch =
internal::chrono::floor<internal::chrono::days>(std::chrono::milliseconds{millis});
return std::make_shared<Date32Scalar>(static_cast<int32_t>(days_since_epoch.count()),
std::move(to_type));
}

// date to timestamp
template <typename To, typename From>
enable_if_timestamp<Result<std::shared_ptr<To>>> CastImpl(
enable_if_timestamp<To, Result<std::shared_ptr<Scalar>>> CastImpl(
const DateScalar<From>& from, std::shared_ptr<DataType> to_type) {
using ToScalar = typename TypeTraits<To>::ScalarType;
int64_t millis = from.value;
Expand Down
45 changes: 45 additions & 0 deletions cpp/src/arrow/scalar_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -973,6 +973,51 @@ TEST(TestDateScalars, MakeScalar) {
Date64Scalar(-188171LL * 24 * 60 * 60 * 1000));
}

TEST(TestDateScalars, CastTo) {
Comment thread
HuaHuaY marked this conversation as resolved.
constexpr int64_t kMillisecondsInDay = 86400000;

ASSERT_OK_AND_ASSIGN(auto casted_date64, Date32Scalar(2).CastTo(date64()));
EXPECT_EQ(*casted_date64, Date64Scalar(2 * kMillisecondsInDay));

ASSERT_OK_AND_ASSIGN(auto casted_date32,
Date64Scalar(2 * kMillisecondsInDay).CastTo(date32()));
EXPECT_EQ(*casted_date32, Date32Scalar(2));

const auto timestamp_type = timestamp(TimeUnit::SECOND);

ASSERT_OK_AND_ASSIGN(auto timestamp_from_date32,
Date32Scalar(2).CastTo(timestamp_type));
EXPECT_EQ(*timestamp_from_date32, TimestampScalar(2 * 24 * 60 * 60, timestamp_type));

ASSERT_OK_AND_ASSIGN(auto timestamp_from_date64,
Date64Scalar(2 * kMillisecondsInDay).CastTo(timestamp_type));
EXPECT_EQ(*timestamp_from_date64, TimestampScalar(2 * 24 * 60 * 60, timestamp_type));

ASSERT_OK_AND_ASSIGN(
auto date64_from_timestamp,
TimestampScalar(2 * kMillisecondsInDay + 3, timestamp(TimeUnit::MILLI))
.CastTo(date64()));
EXPECT_EQ(*date64_from_timestamp, Date64Scalar(2 * kMillisecondsInDay));

ASSERT_OK_AND_ASSIGN(
auto date32_from_timestamp,
TimestampScalar(2 * kMillisecondsInDay + 3, timestamp(TimeUnit::MILLI))
.CastTo(date32()));
EXPECT_EQ(*date32_from_timestamp, Date32Scalar(2));

// 1969-12-31T12:00:00Z -- floor to the previous day instead of truncating to zero.
constexpr int64_t kHalfDay = kMillisecondsInDay / 2;
ASSERT_OK_AND_ASSIGN(
auto negative_date64_from_timestamp,
TimestampScalar(-kHalfDay, timestamp(TimeUnit::MILLI)).CastTo(date64()));
EXPECT_EQ(*negative_date64_from_timestamp, Date64Scalar(-kMillisecondsInDay));

ASSERT_OK_AND_ASSIGN(
auto negative_date32_from_timestamp,
TimestampScalar(-kHalfDay, timestamp(TimeUnit::MILLI)).CastTo(date32()));
EXPECT_EQ(*negative_date32_from_timestamp, Date32Scalar(-1));
}

TEST(TestTimeScalars, Basics) {
auto type1 = time32(TimeUnit::MILLI);
auto type2 = time32(TimeUnit::SECOND);
Expand Down
7 changes: 3 additions & 4 deletions cpp/src/arrow/util/async_generator.h
Original file line number Diff line number Diff line change
Expand Up @@ -1863,7 +1863,7 @@ constexpr int kDefaultBackgroundQRestart = 16;
///
/// This generator will queue up to max_q blocks
template <typename T>
static Result<AsyncGenerator<T>> MakeBackgroundGenerator(
Result<AsyncGenerator<T>> MakeBackgroundGenerator(
Iterator<T> iterator, internal::Executor* io_executor,
int max_q = kDefaultBackgroundMaxQ, int q_restart = kDefaultBackgroundQRestart) {
if (max_q < q_restart) {
Expand All @@ -1887,15 +1887,14 @@ static Result<AsyncGenerator<T>> MakeBackgroundGenerator(
///
/// This generator does not queue
template <typename T>
static Result<AsyncGenerator<T>> MakeBlockingGenerator(
std::shared_ptr<Iterator<T>> iterator) {
Result<AsyncGenerator<T>> MakeBlockingGenerator(std::shared_ptr<Iterator<T>> iterator) {
return [it = std::move(iterator)]() mutable -> Future<T> {
return Future<T>::MakeFinished(it->Next());
};
}

template <typename T>
static Result<AsyncGenerator<T>> MakeBlockingGenerator(Iterator<T> iterator) {
Result<AsyncGenerator<T>> MakeBlockingGenerator(Iterator<T> iterator) {
return MakeBlockingGenerator(std::make_shared<Iterator<T>>(std::move(iterator)));
}

Expand Down
24 changes: 12 additions & 12 deletions cpp/src/arrow/util/bit_block_counter.h
Original file line number Diff line number Diff line change
Expand Up @@ -425,8 +425,8 @@ class ARROW_EXPORT OptionalBinaryBitBlockCounter {
// Functional-style bit block visitors.

template <typename VisitNotNull, typename VisitNull>
static Status VisitBitBlocks(const uint8_t* bitmap, int64_t offset, int64_t length,
VisitNotNull&& visit_not_null, VisitNull&& visit_null) {
Status VisitBitBlocks(const uint8_t* bitmap, int64_t offset, int64_t length,
VisitNotNull&& visit_not_null, VisitNull&& visit_null) {
internal::OptionalBitBlockCounter bit_counter(bitmap, offset, length);
int64_t position = 0;
while (position < length) {
Expand All @@ -453,8 +453,8 @@ static Status VisitBitBlocks(const uint8_t* bitmap, int64_t offset, int64_t leng
}

template <typename VisitNotNull, typename VisitNull>
static void VisitBitBlocksVoid(const uint8_t* bitmap, int64_t offset, int64_t length,
VisitNotNull&& visit_not_null, VisitNull&& visit_null) {
void VisitBitBlocksVoid(const uint8_t* bitmap, int64_t offset, int64_t length,
VisitNotNull&& visit_not_null, VisitNull&& visit_null) {
internal::OptionalBitBlockCounter bit_counter(bitmap, offset, length);
int64_t position = 0;
while (position < length) {
Expand All @@ -480,10 +480,10 @@ static void VisitBitBlocksVoid(const uint8_t* bitmap, int64_t offset, int64_t le
}

template <typename VisitNotNull, typename VisitNull>
static Status VisitTwoBitBlocks(const uint8_t* left_bitmap, int64_t left_offset,
const uint8_t* right_bitmap, int64_t right_offset,
int64_t length, VisitNotNull&& visit_not_null,
VisitNull&& visit_null) {
Status VisitTwoBitBlocks(const uint8_t* left_bitmap, int64_t left_offset,
const uint8_t* right_bitmap, int64_t right_offset,
int64_t length, VisitNotNull&& visit_not_null,
VisitNull&& visit_null) {
if (left_bitmap == NULLPTR || right_bitmap == NULLPTR) {
// At most one bitmap is present
if (left_bitmap == NULLPTR) {
Expand Down Expand Up @@ -524,10 +524,10 @@ static Status VisitTwoBitBlocks(const uint8_t* left_bitmap, int64_t left_offset,
}

template <typename VisitNotNull, typename VisitNull>
static void VisitTwoBitBlocksVoid(const uint8_t* left_bitmap, int64_t left_offset,
const uint8_t* right_bitmap, int64_t right_offset,
int64_t length, VisitNotNull&& visit_not_null,
VisitNull&& visit_null) {
void VisitTwoBitBlocksVoid(const uint8_t* left_bitmap, int64_t left_offset,
const uint8_t* right_bitmap, int64_t right_offset,
int64_t length, VisitNotNull&& visit_not_null,
VisitNull&& visit_null) {
if (left_bitmap == NULLPTR || right_bitmap == NULLPTR) {
// At most one bitmap is present
if (left_bitmap == NULLPTR) {
Expand Down
2 changes: 1 addition & 1 deletion cpp/src/arrow/util/future.h
Original file line number Diff line number Diff line change
Expand Up @@ -702,7 +702,7 @@ class WeakFuture {
/// If a Result<Future> holds an error instead of a Future, construct a finished Future
/// holding that error.
template <typename T>
static Future<T> DeferNotOk(Result<Future<T>> maybe_future) {
Future<T> DeferNotOk(Result<Future<T>> maybe_future) {
if (ARROW_PREDICT_FALSE(!maybe_future.ok())) {
return Future<T>::MakeFinished(std::move(maybe_future).status());
}
Expand Down
14 changes: 10 additions & 4 deletions cpp/src/gandiva/decimal_ir.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "arrow/util/logging_internal.h"
#include "gandiva/decimal_ir.h"
#include "gandiva/decimal_type_util.h"
#include "gandiva/llvm_util_internal.h"

// Algorithms adapted from Apache Impala

Expand Down Expand Up @@ -424,9 +425,15 @@ Status DecimalIR::BuildCompare(const std::string& function_name,
llvm::Value* DecimalIR::CallDecimalFunction(const std::string& function_name,
llvm::Type* return_type,
const std::vector<llvm::Value*>& params) {
auto create_call = [&](const std::vector<llvm::Value*>& args) {
auto* call = ir_builder()->CreateCall(module()->getFunction(function_name), args);
internal::CopyZExtAttrs(*call->getCalledFunction(), *call);
return call;
};

if (kDecimalIRBuilderFunctions.count(function_name) != 0) {
// this is fn built with the irbuilder.
return ir_builder()->CreateCall(module()->getFunction(function_name), params);
return create_call(params);
}

// ppre-compiler fn : disassemble i128 to two i64s and re-assemble.
Expand Down Expand Up @@ -454,7 +461,7 @@ llvm::Value* DecimalIR::CallDecimalFunction(const std::string& function_name,
dis_assembled_args.push_back(out_low_ptr);

// Make call to pre-compiled IR function.
ir_builder()->CreateCall(module()->getFunction(function_name), dis_assembled_args);
create_call(dis_assembled_args);

auto out_high = ir_builder()->CreateLoad(i64, out_high_ptr);
auto out_low = ir_builder()->CreateLoad(i64, out_low_ptr);
Expand All @@ -463,8 +470,7 @@ llvm::Value* DecimalIR::CallDecimalFunction(const std::string& function_name,
DCHECK_NE(return_type, types()->void_type());

// Make call to pre-compiled IR function.
result = ir_builder()->CreateCall(module()->getFunction(function_name),
dis_assembled_args);
result = create_call(dis_assembled_args);
}
return result;
}
Expand Down
Loading
Loading