Skip to content

[Runtime] Add function_cast, switch from std::bit_cast. #80516

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

Merged
merged 1 commit into from
Apr 11, 2025
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
51 changes: 51 additions & 0 deletions include/swift/Basic/Casting.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//===--- Casting.h - Helpers for casting ------------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_BASIC_CASTING_H
#define SWIFT_BASIC_CASTING_H

#include <type_traits>

namespace swift {

/// Cast between two function types. Use in place of std::bit_cast, which
/// doesn't work on ARM64e with address-discriminated signed function types.
///
/// Address-discriminated ptrauth attributes can only be applied to values with
/// a defined storage location, such as struct fields, since their value is
/// inherently tied to their address. They can't be applied to function
/// paremeters. When passing such a value to a templated function, the ptrauth
/// attribute disappears from the inferred type.
///
/// bit_cast takes the source by reference, which means that the ptrauth
/// attribute remains on the inferred type, and the value is not trivially
/// copyable.
///
/// function_cast instead takes the source by value, avoiding that issue and
/// ensuring that passed-in function pointers are always trivially copyable.
template <typename Destination, typename Source>
Destination function_cast(Source source) {
static_assert(sizeof(Destination) == sizeof(Source),
"Source and destination must be the same size");
static_assert(std::is_trivially_copyable_v<Source>,
"The source type must be trivially constructible");
static_assert(std::is_trivially_copyable_v<Destination>,
"The destination type must be trivially constructible");

Destination destination;
memcpy(&destination, &source, sizeof(source));
return destination;
}

}

#endif
45 changes: 0 additions & 45 deletions include/swift/Runtime/STLCompatibility.h

This file was deleted.

12 changes: 6 additions & 6 deletions stdlib/public/Concurrency/AsyncLet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@
#include "swift/ABI/Metadata.h"
#include "swift/ABI/Task.h"
#include "swift/ABI/TaskOptions.h"
#include "swift/Basic/Casting.h"
#include "swift/Runtime/Heap.h"
#include "swift/Runtime/HeapObject.h"
#include "swift/Runtime/STLCompatibility.h"
#include "swift/Threading/Mutex.h"
#include "llvm/ADT/PointerIntPair.h"

Expand Down Expand Up @@ -289,7 +289,7 @@ static void _asyncLet_get_throwing_continuation(

// Continue the caller's execution.
auto throwingResume =
std::bit_cast<ThrowingTaskFutureWaitContinuationFunction*>(callContext->ResumeParent);
function_cast<ThrowingTaskFutureWaitContinuationFunction*>(callContext->ResumeParent);
return throwingResume(callContext->Parent, error);
}

Expand All @@ -307,7 +307,7 @@ static void swift_asyncLet_get_throwingImpl(

auto aletContext = static_cast<AsyncLetContinuationContext*>(callContext);
aletContext->ResumeParent =
std::bit_cast<TaskContinuationFunction*>(resumeFunction);
function_cast<TaskContinuationFunction*>(resumeFunction);
aletContext->Parent = callerContext;
aletContext->alet = alet;
auto futureContext = asImpl(alet)->getFutureContext();
Expand Down Expand Up @@ -377,7 +377,7 @@ static void asyncLet_finish_after_task_completion(SWIFT_ASYNC_CONTEXT AsyncConte
swift_task_dealloc(task);
}

return std::bit_cast<ThrowingTaskFutureWaitContinuationFunction*>(resumeFunction)
return function_cast<ThrowingTaskFutureWaitContinuationFunction*>(resumeFunction)
(callerContext, error);
}

Expand Down Expand Up @@ -529,14 +529,14 @@ static void swift_asyncLet_consume_throwingImpl(
if (asImpl(alet)->hasResultInBuffer()) {
return asyncLet_finish_after_task_completion(callerContext,
alet,
std::bit_cast<TaskContinuationFunction*>(resumeFunction),
function_cast<TaskContinuationFunction*>(resumeFunction),
callContext,
nullptr);
}

auto aletContext = static_cast<AsyncLetContinuationContext*>(callContext);
aletContext->ResumeParent =
std::bit_cast<TaskContinuationFunction*>(resumeFunction);
function_cast<TaskContinuationFunction*>(resumeFunction);
aletContext->Parent = callerContext;
aletContext->alet = alet;
auto futureContext = asImpl(alet)->getFutureContext();
Expand Down
6 changes: 3 additions & 3 deletions stdlib/public/Concurrency/DispatchGlobalExecutor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@

#include <cstddef>

#include "swift/Basic/Casting.h"
#include "swift/Runtime/Concurrency.h"
#include "swift/Runtime/EnvironmentVariables.h"
#include "swift/Runtime/STLCompatibility.h"

#if SWIFT_CONCURRENCY_ENABLE_DISPATCH
#include "swift/Runtime/HeapObject.h"
Expand Down Expand Up @@ -99,11 +99,11 @@ static void initializeDispatchEnqueueFunc(dispatch_queue_t queue, void *obj,
if (SWIFT_RUNTIME_WEAK_CHECK(dispatch_async_swift_job))
func = SWIFT_RUNTIME_WEAK_USE(dispatch_async_swift_job);
#elif defined(_WIN32)
func = std::bit_cast<dispatchEnqueueFuncType>(
func = function_cast<dispatchEnqueueFuncType>(
GetProcAddress(LoadLibraryW(L"dispatch.dll"),
"dispatch_async_swift_job"));
#else
func = std::bit_cast<dispatchEnqueueFuncType>(
func = function_cast<dispatchEnqueueFuncType>(
dlsym(RTLD_NEXT, "dispatch_async_swift_job"));
#endif
#endif
Expand Down
7 changes: 4 additions & 3 deletions stdlib/public/Concurrency/Task.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "swift/ABI/Metadata.h"
#include "swift/ABI/Task.h"
#include "swift/ABI/TaskOptions.h"
#include "swift/Basic/Casting.h"
#include "swift/Basic/Lazy.h"
#include "swift/Runtime/Concurrency.h"
#include "swift/Runtime/EnvironmentVariables.h"
Expand Down Expand Up @@ -1073,19 +1074,19 @@ swift_task_create_commonImpl(size_t rawTaskCreateFlags,
// The final funclet shouldn't release the task or the task function.
} else if (asyncLet) {
initialContext->ResumeParent =
reinterpret_cast<TaskContinuationFunction*>(&completeTask);
function_cast<TaskContinuationFunction *>(&completeTask);

// If we have a non-null closure context and the task function is not
// consumed by calling it, use a final funclet that releases both the
// task and the closure context.
} else if (closureContext && !taskCreateFlags.isTaskFunctionConsumed()) {
initialContext->ResumeParent =
reinterpret_cast<TaskContinuationFunction*>(&completeTaskWithClosure);
function_cast<TaskContinuationFunction *>(&completeTaskWithClosure);

// Otherwise, just release the task.
} else {
initialContext->ResumeParent =
reinterpret_cast<TaskContinuationFunction*>(&completeTaskAndRelease);
function_cast<TaskContinuationFunction *>(&completeTaskAndRelease);
}
#pragma clang diagnostic pop

Expand Down
8 changes: 4 additions & 4 deletions stdlib/public/Concurrency/TaskGroup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@
#include "swift/ABI/Task.h"
#include "swift/ABI/TaskGroup.h"
#include "swift/ABI/TaskOptions.h"
#include "swift/Basic/Casting.h"
#include "swift/Basic/RelativePointer.h"
#include "swift/Basic/STLExtras.h"
#include "swift/Runtime/Concurrency.h"
#include "swift/Runtime/Config.h"
#include "swift/Runtime/Heap.h"
#include "swift/Runtime/HeapObject.h"
#include "swift/Runtime/STLCompatibility.h"
#include "swift/Threading/Mutex.h"
#include <atomic>
#include <deque>
Expand Down Expand Up @@ -1661,7 +1661,7 @@ task_group_wait_resume_adapter(SWIFT_ASYNC_CONTEXT AsyncContext *_context) {

auto context = static_cast<TaskFutureWaitAsyncContext *>(_context);
auto resumeWithError =
std::bit_cast<AsyncVoidClosureResumeEntryPoint *>(context->ResumeParent);
function_cast<AsyncVoidClosureResumeEntryPoint *>(context->ResumeParent);
return resumeWithError(context->Parent, context->errorResult);
}

Expand Down Expand Up @@ -1713,7 +1713,7 @@ static void swift_taskGroup_wait_next_throwingImpl(

auto context = static_cast<TaskFutureWaitAsyncContext *>(rawContext);
context->ResumeParent =
std::bit_cast<TaskContinuationFunction *>(resumeFunction);
function_cast<TaskContinuationFunction *>(resumeFunction);
context->Parent = callerContext;
context->errorResult = nullptr;
context->successResultPointer = resultPointer;
Expand Down Expand Up @@ -1945,7 +1945,7 @@ void TaskGroupBase::waitAll(SwiftError* bodyError, AsyncTask *waitingTask,

auto context = static_cast<TaskFutureWaitAsyncContext *>(rawContext);
context->ResumeParent =
std::bit_cast<TaskContinuationFunction *>(resumeFunction);
function_cast<TaskContinuationFunction *>(resumeFunction);
context->Parent = callerContext;
context->errorResult = nullptr;
context->successResultPointer = resultPointer;
Expand Down
8 changes: 4 additions & 4 deletions stdlib/public/Distributed/DistributedActor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
#include "swift/ABI/Actor.h"
#include "swift/ABI/Metadata.h"
#include "swift/ABI/Task.h"
#include "swift/Basic/Casting.h"
#include "swift/Runtime/AccessibleFunction.h"
#include "swift/Runtime/Concurrency.h"
#include "swift/Runtime/STLCompatibility.h"

using namespace swift;

Expand Down Expand Up @@ -103,7 +103,7 @@ static void swift_distributed_execute_target_resume(
SWIFT_CONTEXT SwiftError *error) {
auto parentCtx = context->Parent;
auto resumeInParent =
std::bit_cast<TargetExecutorSignature::ContinuationType *>(
function_cast<TargetExecutorSignature::ContinuationType *>(
parentCtx->ResumeParent);
swift_task_dealloc(context);
// See `swift_distributed_execute_target` - `parentCtx` in this case
Expand Down Expand Up @@ -132,7 +132,7 @@ void swift_distributed_execute_target(
SwiftError *error =
swift_distributed_makeDistributedTargetAccessorNotFoundError();
auto resumeInParent =
std::bit_cast<TargetExecutorSignature::ContinuationType *>(
function_cast<TargetExecutorSignature::ContinuationType *>(
callerContext->ResumeParent);
resumeInParent(callerContext, error);
return;
Expand All @@ -150,7 +150,7 @@ void swift_distributed_execute_target(
swift_task_alloc(asyncFnPtr->ExpectedContextSize));

calleeContext->Parent = callerContext;
calleeContext->ResumeParent = std::bit_cast<TaskContinuationFunction *>(
calleeContext->ResumeParent = function_cast<TaskContinuationFunction *>(
&swift_distributed_execute_target_resume);

accessorEntry(calleeContext, argumentDecoder, argumentTypes, resultBuffer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
// DEP: _exit
// DEP: _free
// DEP: _malloc
// DEP: _memcpy
// DEP: _memmove
// DEP: _memset
// DEP: _memset_s
Expand Down
1 change: 1 addition & 0 deletions test/embedded/dependencies-concurrency.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
// DEP: _exit
// DEP: _free
// DEP: _malloc
// DEP: _memcpy
// DEP: _memmove
// DEP: _memset
// DEP: _memset_s
Expand Down
1 change: 1 addition & 0 deletions test/embedded/dependencies-concurrency2.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
// DEP: _exit
// DEP: _free
// DEP: _malloc
// DEP: _memcpy
// DEP: _memmove
// DEP: _memset
// DEP: _memset_s
Expand Down