chore(#755): re-cut carried patch 0018 for OCCT#1417's review - #759
Merged
Conversation
gkv311 reviewed our upstream PR and asked for three changes: SquareDistance() instead of Distance() in the loop, theTol3d renamed to theTol (the template also instantiates on Adaptor2d_Curve2d), and a choice between two ways to stop duplicating Standard_ConstructionError_Raise_if for a degenerate point count. Measured before choosing on the third point: every bridge call site that constructs GCPnts_UniformAbscissa/GCPnts_QuasiUniformAbscissa with a caller-supplied count already wraps the construction in catch (...), and Issue558SamplingCountBoundsTests asserts the not-done/empty-result contract for exactly this input across all of them, so an unconditional throw and an unconditional not-done are observably identical for our own contract. Took the not-done option: it does not depend on No_Exception being defined, which is the option #555 argued for in the first place. Re-verified via override-link (production flags, -DNDEBUG -DNo_Exception): the 232/6766-configuration equivalence sweep and the degenerate-count sweep both report the same verdict as before this revision. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…validated Answers the issue's third measurement question directly: grepped occt-src for every count-based GCPnts_UniformAbscissa/GCPnts_QuasiUniformAbscissa call site outside the GCPnts package itself. All 5 production sites and the 1 Draw test command already hardcode or clamp/reject the count before construction, so choosing (a) over (b) would not have changed behavior for any caller found in the tree, on top of not changing it for OCCTSwift's own bridge. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…orts Two edits to outward-facing text. The opener described our own repro path and a force-push we have not done. A maintainer needs neither; the branch state speaks for itself once it is pushed. The substantive one: having checked every in-tree construction site, the finding is not that option (a) "would not change observed behavior for any caller" but that no caller can reach the degenerate branch at all, because each hardcodes or rejects the count first, `uniformAbscissa`'s Draw handler included with its own `if (nocp < 2) return 1;`. That makes the choice between the two options free of consequence for OCCT itself, which is a stronger and more useful thing to tell a reviewer than a statement about callers.
This was referenced Aug 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Maintainer gkv311 reviewed
our upstream OCCT PR (
Scripts/patches/0018-*, filed from #555) and asked for three changes. Twowere mechanical: compare
SquareDistance()against a tolerance squared once outside the loopinstead of calling
Distance()inside it, and rename thePerformparametertheTol3dtotheTolsince the same template also instantiates on
Adaptor2d_Curve2d. The third needed a decision, notjust compliance: whether a degenerate point count should start throwing
Standard_ConstructionErrorin every build, or answer not-done in every build, to stop duplicating
Standard_ConstructionError_Raise_if(an assert-grade macro this project's own build compiles awayvia
BUILD_RELEASE_DISABLE_EXCEPTIONS=ON).This PR re-cuts the carried patch to match the review, re-verifies it (override-link, not a full
kernel rebuild), and prepares (but does not push or post) the follow-up upstream commit and reply.
Closes #755
The decision on point 1, with the measurement
Measured before choosing, per the issue's own instruction:
GCPnts_UniformAbscissa/GCPnts_QuasiUniformAbscissawith a caller-supplied count already wraps the construction in
catch (...). GreppedSources/OCCTBridge/src/*.mmfor both classes: 9 direct construction sites(
OCCTBridge_Curve3D.mmlines 835, 1135, 1724, 3076, 3092, 3109, 3126, 5743;OCCTBridge_Geom2d.mmline 5237). 8 are directly insidetry { ... } catch (...) { return 0/nullptr/false; }. The 9th (OCCTBridge_Curve3D.mm:5741, thestatichelpersampleAdaptorUniform) is not itself wrapped, but its comment says so explicitly ("Callers keeptheir own try/catch ... so a thrown Standard_Failure/StdFail_NotDone can never cross the extern
"C" boundary") and both its callers (
OCCTBridge_Curve3D.mm:5806,:5858) are:try { return sampleAdaptorUniform(...); }. So all 11 reachable paths (9 + 2 via the helper) sitinside a
catch (...)today.Tests/OCCTCurveTests/Issue558SamplingCountBoundsTests.swiftasserts the not-done/empty-resultcontract for exactly this input.
curve3DRequests,curve2DRequests,edgeRequests,uniformAbscissaRequestsall iteraten in [-1, 0, 1, ...](1 is the degenerate case this guardcovers) and assert
.count == 0/nil.initialize()with an unvalidated count. GreppedLibraries/occt-src/srcfor both classes outside their ownGCPntspackage: 5 production callsites (
PrsDim_AngleDimension.cxx,BRepFill_TrimShellCorner.cxx,BRepExtrema_ProximityValueTool.cxx,BRepBuilderAPI_Sewing.cxx,ShapeAnalysis_CanonicalRecognition.cxx,ProjLib_ComputeApproxOnPolarSurface.cxx) and one Drawtest command (
GeometryTest_CurveCommands.cxx). Every one either hardcodes the count(
N = 40,NbOfPnts = 61,npt = 4/8) or clamps it before construction(
std::max(2, aNbPoints),std::max(3, aNbSamplePoints), the Draw command's ownif (aSrcNbPnts < 2) { ...; return 1; }). So today, choosing (a) over (b) would not have changedbehavior for any caller found in the tree either, on top of not changing it for OCCTSwift.
Given both, a caller-visible degenerate-count result is identical today whether the kernel throws
(caught, converted to the empty/nil result) or returns not-done directly (the same empty/nil
result), so both options were safe for OCCTSwift's own contract, and the choice had to be made on a
different ground, not "which one breaks us."
Chose (b), replacing
Standard_ConstructionError_Raise_ifoutright rather than adding anunconditional
throwbeside it: it is the option that does not depend onNo_Exceptionbeingdefined (we build with
BUILD_RELEASE_DISABLE_EXCEPTIONS=ON, so option (a) would make a degeneratecount throw in our own build for the first time), and it is what #555 argued for originally, a
silent not-done rather than an exception.
Confirmed directly, not assumed: compiled the pre-review and revised patch without
No_Exceptionand constructed each class with a degenerate count. Pre-review patch: still throwsStandard_ConstructionError(the un-replacedRaise_ifis live withoutNo_Exception). Revisedpatch: throws in neither build configuration.
Verification
Override-link, not a full rebuild (the issue's own guidance; a full
Scripts/build-occt.shis~75 minutes and not warranted for a review-response patch). Reconstructed the pristine (pre-0018)
files by reverse-applying the existing patch (
patch -p1 -R), confirmed the round-trip is exact(re-applying the pre-review patch reproduces the current
occt-srcbyte for byte), then hand-editedthe pristine files per the review and diffed to produce the revised patch. Compiled three variants
of both files (pristine / current unrevised / revised) with the production flags
(
-DNDEBUG -DNo_Exception, matchingBUILD_RELEASE_DISABLE_EXCEPTIONS=ON) and override-linked eachahead of
libOCCT-macos.a:Scripts/repro/555-gcpnts-count-contract/repro_555_count.mm: byte-identical output between thecurrent (unrevised) and revised override-linked binaries. Over-request 0/60 for both classes on
the pathological ellipse; every degenerate count (0, 1, -3) on every one of 5 curve types returns
done=0for both classes, no crash.Scripts/repro/555-gcpnts-count-contract/repro_555_equivalence.mm(17 curve types x point counts2-200 x both classes, 6766 configurations x 2 = 13532 lines): diffed the current and revised
outputs directly, 0 lines differ.
This worktree has a local kernel:
Librariesis a symlink to the shared checkout'sLibraries/(created for this task;
.gitignored, not part of the diff), which hasocct-srcand a rebuiltOCCT.xcframeworkalready carrying the pre-review 0018.OCCTSWIFT_LOCAL=1was set for everyswift build/swift testinvocation below. The rebuilt xcframework itself was not rebuilt withthe revised patch in this PR (that is a separate, deliberately-deferred step per the release
process); the override-link technique is what lets the revised kernel source be measured without
that rebuild.
Swift-side (against the current, unrevised-kernel
Libraries/OCCT.xcframework; this PR touchesno bridge or Swift source, so this checks for regressions from the patch-file/doc changes only):
swift build --target OCCTCurveTests: clean (pre-existing unrelated warnings only).swift test --filter "Issue558SamplingCountBounds": 21/21 pass.swift test --filter "QuasiUniformAbscissaTests|GCPntsQuasiUniformTests|UniformAbscissaTests":7/7 pass.
swift test: 5462 tests in 1423 suites, all pass.Gate scripts (all five, plus the four
--self-tests, plus the census and merge-history audit--self-tests, perCLAUDE.md's "Static Gate Scripts"):check-bridge-index.pycheck-bridge-index.py --self-testcheck-null-handle-guards.pycheck-null-handle-guards.py --self-testcheck-docs-defaults.pycheck-docs-defaults.py --self-testderive-bridge-header-split.py --verifyderive-bridge-header-split.py --self-testcount-operations.py(no--self-test, seeCLAUDE.md)census-unmeasured-values.py --self-testcheck-changelog-transcription.py --self-testNo new test or
--self-testcase is added in this PR (see Checklist), sookf/policies/prove-the-test-fails.md has nothing new to
apply here; the equivalence sweep above is the "prove it did not move" evidence for a diff that
does not add coverage of its own.
CHANGELOG entry
None. This revises an internal carried OCCT source patch
(
Scripts/patches/0018-GCPnts-degenerate-count-and-duplicate-end-point-555.patch) in response toupstream review, before the corresponding kernel rebuild ships. No OCCTSwift public API or behavior
changes: verified by the equivalence sweep above (0 of 13532 lines differ from the pre-review
patch) and the full
swift testrun (5462 tests, all pass, against the unrebuilt kernel this PRdoes not touch).
SemVer impact
NONE. No public Swift API changes, no OCCTSwift-observable behavior changes. The touched files are
an internal carried-patch text file, its own README section, a reproducer's README, and new
prepared-but-unpushed upstream artifacts.
Checklist
behavior changes (see "Verification" and the equivalence sweep).
--self-testcase was run once with its subject broken: N/A, nonew test or
--self-testcase is added in this PR.docs/CHANGELOG.mdis not in this diff.docs/SEMVER.mdis not in this diff.Notes for the reviewer
gsdali/OCCT, nothing was posted toOCCT#1417. The prepared follow-up commit
(
git am-verified against a throwaway repo built from the reconstructed pristine files) and thedrafted reply are both under
Scripts/repro/555-gcpnts-count-contract/upstream/,ready for a human to push/post.
Libraries/OCCT.xcframeworkstill carries the pre-review 0018. Rebuilding it withthe revised patch is a separate step (a real
Scripts/build-occt.shrun, ~75 minutes), deliberatelyout of scope here per the issue; the override-link measurements above are what stand in for that
rebuild until it happens, most likely alongside a future kernel re-pin.
Performis a freestaticfunction at namespace scope,not a member of
GCPnts_UniformAbscissa, so renaming itstheTol3dparameter totheTolintroduces no shadowing against either
initialize()overload's owntheTolparameter (both callsites already pass
theTolstraight through as a plain expression, not through an intermediatelocal of the same name).
Scripts/patches/README.md's0018section and the repro's ownREADME.mdare both updated inplace (not a new patch number), matching this project's existing convention for a revised carried
patch (e.g. the
0011/#363and0016/#374entries).