From 073d1b2f137abff030977350cf8181ec47b5b9f4 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Fri, 28 Aug 2026 18:47:58 +0100 Subject: [PATCH 1/7] fix: support base components in offsetof expressions --- .../clang/parsers/data/ClavaDataParsers.java | 4 ++ .../ast/expr/data/offsetof/OffsetOfBase.java | 38 +++++++++++++++++++ .../expr/data/offsetof/OffsetOfComponent.java | 2 + 3 files changed, 44 insertions(+) create mode 100644 ClavaAst/src/pt/up/fe/specs/clava/ast/expr/data/offsetof/OffsetOfBase.java diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/parsers/data/ClavaDataParsers.java b/ClangAstParser/src/pt/up/fe/specs/clang/parsers/data/ClavaDataParsers.java index 531c6feea..2e3c7c9ff 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/parsers/data/ClavaDataParsers.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/parsers/data/ClavaDataParsers.java @@ -60,6 +60,7 @@ import pt.up.fe.specs.clava.ast.expr.data.designator.Designator; import pt.up.fe.specs.clava.ast.expr.data.designator.FieldDesignator; import pt.up.fe.specs.clava.ast.expr.data.offsetof.OffsetOfArray; +import pt.up.fe.specs.clava.ast.expr.data.offsetof.OffsetOfBase; import pt.up.fe.specs.clava.ast.expr.data.offsetof.OffsetOfComponent; import pt.up.fe.specs.clava.ast.expr.data.offsetof.OffsetOfComponentKind; import pt.up.fe.specs.clava.ast.expr.data.offsetof.OffsetOfField; @@ -459,6 +460,9 @@ public static OffsetOfComponent offsetOfComponent(LineStream lines, ClangAstData case IDENTIFIER: component.set(OffsetOfIdentifier.FIELD_NAME, lines.nextLine()); break; + case BASE: + parserData.getClavaNodes().queueSetNode(component, OffsetOfBase.TYPE, lines.nextLine()); + break; default: throw new NotImplementedException(kind); } diff --git a/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/data/offsetof/OffsetOfBase.java b/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/data/offsetof/OffsetOfBase.java new file mode 100644 index 000000000..4cf8c13bc --- /dev/null +++ b/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/data/offsetof/OffsetOfBase.java @@ -0,0 +1,38 @@ +/** + * Copyright 2026 SPeCS. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package pt.up.fe.specs.clava.ast.expr.data.offsetof; + +import org.suikasoft.jOptions.Datakey.DataKey; +import org.suikasoft.jOptions.Datakey.KeyFactory; + +import pt.up.fe.specs.clava.ast.type.Type; + +public class OffsetOfBase extends OffsetOfComponent { + + /// DATAKEYS BEGIN + + public final static DataKey TYPE = KeyFactory.object("type", Type.class); + + /// DATAKEYS END + + @Override + public String getCode() { + return get(TYPE).getCode(); + } + + @Override + public OffsetOfComponentKind getKind() { + return OffsetOfComponentKind.BASE; + } +} diff --git a/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/data/offsetof/OffsetOfComponent.java b/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/data/offsetof/OffsetOfComponent.java index bfe9330d5..bcd2765a8 100644 --- a/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/data/offsetof/OffsetOfComponent.java +++ b/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/data/offsetof/OffsetOfComponent.java @@ -41,6 +41,8 @@ public static OffsetOfComponent newInstance(OffsetOfComponentKind kind) { return new OffsetOfField(); case IDENTIFIER: return new OffsetOfIdentifier(); + case BASE: + return new OffsetOfBase(); default: throw new NotImplementedException(kind); } From 6c11ea8fe7df5a582528dd2ccf1fb98d4e0fc6c2 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Fri, 28 Aug 2026 22:21:40 +0100 Subject: [PATCH 2/7] fix: omit semantic base from offsetof code --- .../up/fe/specs/clava/ast/expr/data/offsetof/OffsetOfBase.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/data/offsetof/OffsetOfBase.java b/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/data/offsetof/OffsetOfBase.java index 4cf8c13bc..b52ecf056 100644 --- a/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/data/offsetof/OffsetOfBase.java +++ b/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/data/offsetof/OffsetOfBase.java @@ -28,7 +28,8 @@ public class OffsetOfBase extends OffsetOfComponent { @Override public String getCode() { - return get(TYPE).getCode(); + // Clang adds the base to the semantic path, but it is not part of the source designator. + return ""; } @Override From 2149859813347904c81a5b8e948fe5db0d5ef024 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Fri, 28 Aug 2026 22:23:02 +0100 Subject: [PATCH 3/7] fix: ignore empty offsetof components --- .../up/fe/specs/clava/ast/expr/OffsetOfExpr.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/OffsetOfExpr.java b/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/OffsetOfExpr.java index bf5ab5083..2ef7f313f 100644 --- a/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/OffsetOfExpr.java +++ b/ClavaAst/src/pt/up/fe/specs/clava/ast/expr/OffsetOfExpr.java @@ -50,18 +50,20 @@ private String getComponentsCode() { StringBuilder code = new StringBuilder(); - boolean isFirst = true; + boolean hasCode = false; for (OffsetOfComponent component : get(COMPONENTS)) { + String componentCode = component.getCode(); - if (component.isField() && !isFirst) { - code.append("."); + if (componentCode.isEmpty()) { + continue; } - code.append(component.getCode()); - - if (isFirst) { - isFirst = false; + if (component.isField() && hasCode) { + code.append("."); } + + code.append(componentCode); + hasCode = true; } return code.toString(); } From f1e6066e5fd024d66029c0536ed32ef369696110 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Fri, 4 Sep 2026 02:16:45 +0100 Subject: [PATCH 4/7] bump to compatible clang-dumper version --- ClangAstParser/clang-dumper-release.tag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ClangAstParser/clang-dumper-release.tag b/ClangAstParser/clang-dumper-release.tag index 73e74e98f..f254b9b77 100644 --- a/ClangAstParser/clang-dumper-release.tag +++ b/ClangAstParser/clang-dumper-release.tag @@ -1 +1 @@ -v18.1.8_2 +v18.1.8_3 From fcfddcf49eb3896350862514ff4140f976d43370 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Sun, 6 Sep 2026 03:37:29 +0100 Subject: [PATCH 5/7] fix(clang): cache CUDA redistribution manifest reads Read platform checks and manifest consumers through a stable cached manifest file (cuda/redistrib_.json) installed with CacheFiles.installFile, mirroring ClangAstWebResource.getManifest, so repeated isBuiltinCudaSupported-style calls do not re-download the manifest. The staging-based manifest download remains for release installation only. Tests: CudaResourcesTest (17), ClangResourcesTest (27) --- .../pt/up/fe/specs/clang/CudaResources.java | 44 +++++++------------ 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/CudaResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/CudaResources.java index 28641b5ab..30ea7ecc0 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/CudaResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/CudaResources.java @@ -57,7 +57,8 @@ * the bundled dumper. * *

CUDA resources are release-addressed. The published release directory is the complete CUDA installation; - * manifests and downloaded archives used to build it live in staging until that directory is published.

+ * manifests and downloaded archives used to build it live in staging until that directory is published. The + * redistribution manifest itself is cached at {@code /cuda/redistrib_.json} and reused across calls.

*/ final class CudaResources { @@ -117,7 +118,8 @@ static boolean isSupportedPlatform(Path cacheRoot) { } static boolean isSupportedPlatform(Path cacheRoot, FileResourceProvider manifestResource) { - return findSupportedPlatform(getCurrentManifest(cacheRoot, manifestResource)).isPresent(); + return findSupportedPlatform(readManifest(cacheRoot, ClangAstWebResource.getCudaReleaseTag(), + manifestResource)).isPresent(); } static CudaPlatform getCurrentPlatform() { @@ -125,7 +127,8 @@ static CudaPlatform getCurrentPlatform() { } static CudaPlatform getCurrentPlatform(Path cacheRoot) { - return requireSupportedPlatform(getCurrentManifest(cacheRoot)); + var releaseTag = ClangAstWebResource.getCudaReleaseTag(); + return requireSupportedPlatform(readManifest(cacheRoot, releaseTag, getManifestResource(releaseTag))); } static CudaPlatform getCurrentPlatform(NvidiaCudaManifest manifest) { @@ -183,30 +186,17 @@ private static RuntimeException unsupportedPlatform(NvidiaCudaManifest manifest, + ". Available manifest platform keys: " + getAvailablePlatformKeys(manifest)); } - private static NvidiaCudaManifest getCurrentManifest(Path cacheRoot) { - var releaseTag = ClangAstWebResource.getCudaReleaseTag(); - return getCurrentManifest(cacheRoot, releaseTag, getManifestResource(releaseTag)); - } - - private static NvidiaCudaManifest getCurrentManifest(Path cacheRoot, FileResourceProvider manifestResource) { - var releaseTag = ClangAstWebResource.getCudaReleaseTag(); - return getCurrentManifest(cacheRoot, releaseTag, manifestResource); - } - - private static NvidiaCudaManifest getCurrentManifest(Path cacheRoot, String releaseTag, - FileResourceProvider manifestResource) { - var cudaRoot = cacheRoot.resolve(CUDA_FOLDERNAME); - CacheFiles.deleteUnlockedStagingLocks(cacheRoot, cudaRoot); - var stagingDirectory = CacheFiles.createStagingDirectory(cacheRoot, cudaRoot, "." + releaseTag + ".tmp-"); - try { - return downloadManifest(cacheRoot, stagingDirectory.path(), releaseTag, manifestResource); - } finally { - try { - CacheFiles.delete(stagingDirectory.path()); - } finally { - stagingDirectory.close(); - } - } + // The manifest is installed to a stable cache path and reused across calls, so platform checks and the like do + // not re-download it on every invocation. Mirrors how ClangAstWebResource.getManifest caches the clang-dumper + // release manifest. + private static NvidiaCudaManifest readManifest(Path cacheRoot, String releaseTag, + FileResourceProvider manifestResource) { + var manifestFile = CacheFiles.installFile(cacheRoot, + cacheRoot.resolve(CUDA_FOLDERNAME).resolve(getManifestFilename(releaseTag)).toFile(), + manifestResource, null, "NVIDIA CUDA redistribution manifest"); + var manifest = parseManifest(SpecsIo.read(manifestFile)); + validateManifest(manifest, releaseTag); + return manifest; } private static boolean isCompatiblePlatform(String manifestPlatform, SupportedPlatform hostPlatform, From faf63a7f3b09275274b3705b1308652d210e8e6e Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Sun, 6 Sep 2026 03:37:32 +0100 Subject: [PATCH 6/7] fix(clang): guide users when built-in CUDA lacks a system Clang SYSTEM mode with built-in CUDA requires a matching system Clang resource directory for Clang's CUDA wrapper headers. Extend the failure message with the expected Clang command, install hints, and the 'builtin' libc mode alternative so users can self-serve. The command and version in the message are derived from the dumper's LLVM major at runtime; the 'apt install clang++-18' wording users actually see is just an example of the rendered output. Tests: CudaResourcesTest (17), ClangResourcesTest (27) --- .../src/pt/up/fe/specs/clang/ClangResources.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java index 041f66e56..daf9ae46b 100644 --- a/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java +++ b/ClangAstParser/src/pt/up/fe/specs/clang/ClangResources.java @@ -363,9 +363,14 @@ private File findSystemClangResourceDir(Integer llvmMajor) { var expectedVersion = llvmMajor == null ? "the local clang-dumper build's version" : "LLVM " + llvmMajor; + var installHint = llvmMajor == null ? "clang++" : "clang++-" + llvmMajor; throw new RuntimeException("Could not find a system Clang resource directory for SYSTEM mode with built-in CUDA" + " on host '" + SupportedPlatform.getCurrentPlatform() + "' (expected " + expectedVersion - + "). Tried: " + commandNames); + + "). Tried: " + commandNames + + ". SYSTEM mode does not bundle Clang's CUDA wrapper headers, so a matching system Clang" + + " installation is required: install '" + installHint + "' (e.g. 'apt install " + installHint + + "' or 'brew install llvm'), or set the libc mode to 'builtin' to use the bundled includes" + + " instead of the system libc"); } private static List getSystemClangCommandNames(Integer llvmMajor) { From 06301ce7c36f2abe5a3d73d95a07dd19653e0349 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Sun, 6 Sep 2026 03:48:38 +0100 Subject: [PATCH 7/7] Upgrade the clang-dumper version used to fix Windows failures --- ClangAstParser/clang-dumper-release.tag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ClangAstParser/clang-dumper-release.tag b/ClangAstParser/clang-dumper-release.tag index f254b9b77..91967a831 100644 --- a/ClangAstParser/clang-dumper-release.tag +++ b/ClangAstParser/clang-dumper-release.tag @@ -1 +1 @@ -v18.1.8_3 +v18.1.8_4