From 4a755ec6ea2eeb5def8c00571c17d4fd9670940e Mon Sep 17 00:00:00 2001 From: shivaduke28 Date: Sun, 5 Apr 2026 21:19:01 +0900 Subject: [PATCH 1/5] Fix dangling pointers in preprocessor macro and search path handling Reserve vector capacity before the loop to prevent reallocation from invalidating c_str() pointers stored in PreprocessorMacroDesc entries. Also fix the same issue in search path construction. Add preprocessor macro tests. Closes #19 Co-Authored-By: Claude Opus 4.6 (1M context) --- Sources/SwiftSlang/SLGlobalSession.mm | 7 ++ Tests/SwiftSlangTests/SwiftSlangTests.swift | 104 ++++++++++++++++++++ 2 files changed, 111 insertions(+) diff --git a/Sources/SwiftSlang/SLGlobalSession.mm b/Sources/SwiftSlang/SLGlobalSession.mm index 421c0d6..9a43bc6 100644 --- a/Sources/SwiftSlang/SLGlobalSession.mm +++ b/Sources/SwiftSlang/SLGlobalSession.mm @@ -65,6 +65,9 @@ - (nullable SLSession *)createSessionWithDesc:(SLSessionDesc *)desc // Build search paths std::vector searchPaths; std::vector searchPathStrings; // Keep strings alive + NSUInteger searchPathCount = desc.searchPaths.count; + searchPathStrings.reserve(searchPathCount); + searchPaths.reserve(searchPathCount); for (NSString *path in desc.searchPaths) { searchPathStrings.push_back([path UTF8String]); searchPaths.push_back(searchPathStrings.back().c_str()); @@ -74,6 +77,10 @@ - (nullable SLSession *)createSessionWithDesc:(SLSessionDesc *)desc std::vector macros; std::vector macroNames; std::vector macroValues; + NSUInteger macroCount = desc.preprocessorMacros.count; + macroNames.reserve(macroCount); + macroValues.reserve(macroCount); + macros.reserve(macroCount); for (NSString *name in desc.preprocessorMacros) { NSString *value = desc.preprocessorMacros[name]; macroNames.push_back([name UTF8String]); diff --git a/Tests/SwiftSlangTests/SwiftSlangTests.swift b/Tests/SwiftSlangTests/SwiftSlangTests.swift index c4b341a..5eccb55 100644 --- a/Tests/SwiftSlangTests/SwiftSlangTests.swift +++ b/Tests/SwiftSlangTests/SwiftSlangTests.swift @@ -356,6 +356,110 @@ final class SwiftSlangTests: XCTestCase { XCTAssertEqual(texType.getResourceAccess(), .read) } + // MARK: - Preprocessor Macros + + func testPreprocessorMacroAffectsCompilation() throws { + let globalSession = try SLGlobalSession.create() + let profile = globalSession.findProfile("sm_5_0") + let targetDesc = SLTargetDesc(format: .metal, profile: profile) + + let source = """ + uniform float intensity; + [shader("fragment")] + float4 fragMain() : SV_Target { + #ifdef USE_RED + return float4(intensity, 0, 0, 1); + #else + return float4(0, 0, intensity, 1); + #endif + } + """ + + // With USE_RED defined + let sessionDescWithMacro = SLSessionDesc() + sessionDescWithMacro.targets = [targetDesc] + sessionDescWithMacro.preprocessorMacros = ["USE_RED": "1"] + let sessionWith = try globalSession.createSession(with: sessionDescWithMacro) + let moduleWith = try sessionWith.loadModule(fromSourceString: "Test", path: "", source: source) + let epWith = try moduleWith.entryPoint(at: 0) + let compositeWith = try sessionWith.createCompositeComponentType(with: moduleWith, entryPoints: [epWith]) + let linkedWith = try compositeWith.link() + let codeWith = try linkedWith.getTargetCode(0) + + // Without USE_RED defined + let sessionDescWithout = SLSessionDesc() + sessionDescWithout.targets = [targetDesc] + let sessionWithout = try globalSession.createSession(with: sessionDescWithout) + let moduleWithout = try sessionWithout.loadModule(fromSourceString: "Test", path: "", source: source) + let epWithout = try moduleWithout.entryPoint(at: 0) + let compositeWithout = try sessionWithout.createCompositeComponentType(with: moduleWithout, entryPoints: [epWithout]) + let linkedWithout = try compositeWithout.link() + let codeWithout = try linkedWithout.getTargetCode(0) + + // Both should compile successfully but produce different Metal code + XCTAssertGreaterThan(codeWith.count, 0) + XCTAssertGreaterThan(codeWithout.count, 0) + XCTAssertNotEqual(codeWith, codeWithout) + } + + func testMultiplePreprocessorMacros() throws { + let globalSession = try SLGlobalSession.create() + let profile = globalSession.findProfile("sm_5_0") + let targetDesc = SLTargetDesc(format: .metal, profile: profile) + let sessionDesc = SLSessionDesc() + sessionDesc.targets = [targetDesc] + sessionDesc.preprocessorMacros = [ + "RESOLUTION_X": "1920", + "RESOLUTION_Y": "1080", + "SCALE": "2", + ] + let session = try globalSession.createSession(with: sessionDesc) + + let source = """ + [shader("fragment")] + float4 fragMain() : SV_Target { + float x = RESOLUTION_X; + float y = RESOLUTION_Y; + float s = SCALE; + return float4(x, y, s, 1); + } + """ + let module = try session.loadModule(fromSourceString: "Test", path: "", source: source) + let entryPoint = try module.entryPoint(at: 0) + let composite = try session.createCompositeComponentType(with: module, entryPoints: [entryPoint]) + let linked = try composite.link() + let metalCode = try linked.getTargetCode(0) + XCTAssertGreaterThan(metalCode.count, 0) + } + + func testPreprocessorMacroWithValue() throws { + let globalSession = try SLGlobalSession.create() + let profile = globalSession.findProfile("sm_5_0") + let targetDesc = SLTargetDesc(format: .metal, profile: profile) + let sessionDesc = SLSessionDesc() + sessionDesc.targets = [targetDesc] + sessionDesc.preprocessorMacros = ["CHANNEL_COUNT": "3"] + let session = try globalSession.createSession(with: sessionDesc) + + let source = """ + uniform float values[CHANNEL_COUNT]; + [shader("fragment")] + float4 fragMain() : SV_Target { return float4(values[0], values[1], values[2], 1); } + """ + let module = try session.loadModule(fromSourceString: "Test", path: "", source: source) + let entryPoint = try module.entryPoint(at: 0) + let composite = try session.createCompositeComponentType(with: module, entryPoints: [entryPoint]) + let linked = try composite.link() + let params = try linked.getShaderParameters(0) + + let valuesParam = params.first { $0.name == "values" } + XCTAssertNotNil(valuesParam) + let typeLayout = try XCTUnwrap(valuesParam?.typeLayout) + let type = try XCTUnwrap(typeLayout.getType()) + XCTAssertEqual(type.getKind(), .array) + XCTAssertEqual(type.getElementCount(), 3) + } + // MARK: - Entry Point func testComputeEntryPoint() throws { From c5789ab53ac822bc22eb90e617bd6c71e7eaec67 Mon Sep 17 00:00:00 2001 From: shivaduke28 Date: Sun, 5 Apr 2026 21:21:49 +0900 Subject: [PATCH 2/5] Switch CI test destination from iOS Simulator to macOS The macos-15 runner no longer includes iOS Simulator runtimes with Xcode 16.2, causing destination lookup failures. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fbf6b4c..71e4db1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -35,5 +35,5 @@ jobs: run: | xcodebuild test \ -scheme SwiftSlang-Package \ - -destination 'platform=iOS Simulator,name=iPhone 16' \ + -destination 'platform=macOS' \ -skipPackagePluginValidation From ffcb926734064a4f8e9006a1288c7b6bc1e69065 Mon Sep 17 00:00:00 2001 From: shivaduke28 Date: Sun, 5 Apr 2026 21:22:54 +0900 Subject: [PATCH 3/5] Install iOS Simulator runtime in CI before running tests The XCFramework only includes iOS libraries, so macOS destination cannot be used. Install the iOS platform to make the simulator available on macos-15 runners. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/test.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 71e4db1..f375e59 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,6 +21,9 @@ jobs: - name: Show Xcode version run: xcodebuild -version + - name: Install iOS Simulator runtime + run: xcodebuild -downloadPlatform iOS + - name: Cache Swift packages uses: actions/cache@v4 with: @@ -35,5 +38,5 @@ jobs: run: | xcodebuild test \ -scheme SwiftSlang-Package \ - -destination 'platform=macOS' \ + -destination 'platform=iOS Simulator,name=iPhone 16' \ -skipPackagePluginValidation From 1aef79bd548bdedd8a15fb51190e5b36aadef7d3 Mon Sep 17 00:00:00 2001 From: shivaduke28 Date: Sun, 5 Apr 2026 21:25:17 +0900 Subject: [PATCH 4/5] Cache iOS Simulator runtime in CI to avoid repeated downloads Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/test.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f375e59..86eca01 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,7 +21,15 @@ jobs: - name: Show Xcode version run: xcodebuild -version + - name: Cache iOS Simulator runtime + id: cache-ios-runtime + uses: actions/cache@v4 + with: + path: ~/Library/Developer/CoreSimulator/Profiles/Runtimes + key: ${{ runner.os }}-ios-simulator-xcode-16.2 + - name: Install iOS Simulator runtime + if: steps.cache-ios-runtime.outputs.cache-hit != 'true' run: xcodebuild -downloadPlatform iOS - name: Cache Swift packages From 55b0a59f83671176fe7860637a6b84697736ca93 Mon Sep 17 00:00:00 2001 From: shivaduke28 Date: Sun, 5 Apr 2026 21:28:15 +0900 Subject: [PATCH 5/5] Upgrade CI from Xcode 16.2 to 16.4 The macos-15 runner no longer includes iOS Simulator runtimes for Xcode 16.2. Use Xcode 16.4 which has pre-installed runtimes available. Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/test.yml | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 86eca01..3d4a309 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,23 +15,12 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Select Xcode 16.2 - run: sudo xcode-select -s /Applications/Xcode_16.2.app + - name: Select Xcode 16.4 + run: sudo xcode-select -s /Applications/Xcode_16.4.app - name: Show Xcode version run: xcodebuild -version - - name: Cache iOS Simulator runtime - id: cache-ios-runtime - uses: actions/cache@v4 - with: - path: ~/Library/Developer/CoreSimulator/Profiles/Runtimes - key: ${{ runner.os }}-ios-simulator-xcode-16.2 - - - name: Install iOS Simulator runtime - if: steps.cache-ios-runtime.outputs.cache-hit != 'true' - run: xcodebuild -downloadPlatform iOS - - name: Cache Swift packages uses: actions/cache@v4 with: