This PR implements `expandSymlink` on Windows with POSIX readlink semantics: it expands exactly one hop and returns the stored link target without resolving the full chain. The main design question was whether Windows symlink expansion should be built on path-finalization APIs such as `GetFinalPathNameByHandleW`, or on direct reparse-point inspection. Current `expandSymlink` is a single-hop "what target is stored in this link object?" operation and most of other ways to resolve symlinks on Windows actually try to answer the "final true file location" question in various slightly-incompatible ways. The full final-path resolution on Windows is substantially more complex than readlink and is planned as a follow-up. ## Implementation choice Implements Windows `expandSymlink` by: - opening the path with `FILE_FLAG_OPEN_REPARSE_POINT` - calling `DeviceIoControl(FSCTL_GET_REPARSE_POINT)` - parsing the reparse payload for `IO_REPARSE_TAG_SYMLINK` and `IO_REPARSE_TAG_MOUNT_POINT` - decoding the UTF-16 slice referenced by the payload - returning the stored target This is the right primitive for the API: - does not depend on whole-path finalization - works for both symlinks and junctions - matches the existing Linux behaviour `widestrs` changes allow using WideCString views without temporary allocations. Windows prohibits symlink creation without admin rights, so, unfortunately, the tests are conditionally skipped by default. Manually running `testament` in an admin console is required. ## Behaviour: - One hop only - Relative symlink targets are returned unchanged - Absolute Windows targets are converted from stored NT-style prefixes to usable Win32 forms when applicable - Non-links, malformed payloads, and unsupported reparse tags raise `OSError` ## Future work Path canonicalization, i.e. "final true file location". Which is, BTW, different from `absolutePath`, which works on paths only and doesn't hit the underlying FS. So this needs to be an API extension. I'd like to follow-up with this when I sort through the docs, for now you can resolve symlinks in a loop. --------- Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
7.7 KiB
v2.x.x - yyyy-mm-dd
Changes affecting backward compatibility
-
-d:nimPreviewFloatRoundtripbecomes the default.system.addFloatandsystem.$now can produce string representations of floating point numbers that are minimal in size and possess round-trip and correct rounding guarantees (via the Dragonbox algorithm). Use-d:nimLegacySprintfto emulate old behaviors. -
The
defaultparameter oftables.getOrDefaulthas been renamed todefto avoid conflicts withsystem.default, so named argument usage for this parameter likegetOrDefault(..., default = ...)will have to be changed. -
With
-d:nimPreviewCheckedClose, theclosefunction in thestd/synciomodule now raises an IO exception in case of an error. -
Unknown warnings and hints now gives warnings
warnUnknownNotesinstead of errors. -
With
-d:nimPreviewAsmSemSymbol, backticked symbols are type checked in theasm/emitstatements. -
The bare
except:now panics onDefect. Useexcept Exception:orexcept Defect:to catchDefect.--legacy:noPanicOnExceptis provided for a transition period. -
With
-d:nimPreviewCStringComparisons, comparsions (<,>,<=,>=) between cstrings switch from reference semantics to value semantics like==and!=. -
std/parsesqlhas been moved to a nimble package, usenimbleoratlasto install it. -
With
-d:nimPreviewDuplicateModuleError, importing two modules that share the same name becomes a compile-time error. This includes importing the same module more than once. Useimport foo as foo1(or other aliases) to avoid collisions. -
Adds the switch
--mangle:nim|cpp, which selectsnimorcppstyle name mangling when used withdebuginfoon, defaults tocpp. -
The second parameter of
succ,pred,inc, anddecinsystemnow acceptsSomeInteger(previouslyOrdinal). -
Bitshift operators (
shl,shr,ashr) now apply bitmasking to the right operand in the C/C++/VM/JS backends. -
Adds a new warning
--warning:ImplicitRangeConversionthat detects downsizing implicit conversions to range types (e.g.,int -> range[0..255]orrange[1..256] -> range[0..255]) that could cause runtime panics. Safe conversions likerange[0..255] -> range[0..65535]and explicit casts do not trigger warnings.inttoNaturalandPositiveconversions do not trigger warnings, which can be enabled with--warning:systemRangeConversion. -
Procedure compatibility also checks the backend representation of the parameter and result types, not just their source-level shape. Use
--legacy:procParamTypeBackendAliasesto restore the older behavior.
Standard library additions and changes
-
Added
system.readRawDataStable, a companion toreadRawDatathat returns a rawptr UncheckedArray[char]into a string's character data which stays valid across moves and copies of the string value. It is available under every string implementation (refc, ARC/ORC and--strings:sso) with the same signature, so code can pin an interior buffer pointer today and be ready for--strings:ssowithoutwhen declaredguards. Under--strings:ssoit promotes a small inline string to its heap representation first; under the other implementations the data is already heap-resident, so it is equivalent toreadRawData. -
setutils.symmetricDifferencealong with its operator versionsetutils.`-+-`and in-place versionsetutils.togglehave been added to more efficiently calculate the symmetric difference of bitsets. -
strutils.multiReplaceoverload for character set replacements in a single pass. Useful for string sanitation. Follows existing multiReplace semantics. -
std/filesadds:- Exports
CopyFlagenum andFilePermissiontype for fine-grained control of file operations - New file operation procs with
Pathsupport:getFilePermissions,setFilePermissionsfor managing permissionstryRemoveFilefor file deletioncopyFilewith configurable buffer size and symlink handlingcopyFileWithPermissionsto preserve file attributescopyFileToDirfor copying files into directories
- Exports
-
std/dirsadds:- New directory operation procs with
Pathsupport:copyDirwith special file handling optionscopyDirWithPermissionsto recursively preserve attributes
- New directory operation procs with
-
system.setLenUninitnow supports refc, JS and VM backends. -
system.setLenUninitfor thestringtype. Allows setting length without initializing new memory on growth. -
std/parseoptnow supports multiple parser modes via aCliModeenum. Modes includeNim(default, fully compatible) and two new experimental modes:LaxandGnufor different option parsing behaviors. -
std/symlinks.expandSymlinknow supports Windows symlinks and junctions with POSIX-like single-hopreadlinksemantics. -
std/nre2is added to replace deprecated NRE. -
system.typeofadds a new parametermodifierModeto specify how type modifiers are handled.
std/mathThe^symbol now supports floating-point as exponent in addition to the Natural type.min,max, andsequtils'minIndex,maxIndexandminmaxforopenArrays now accept a comparison function.system.substrimplementation now usescopymem(wrapped Cmemcpy) for copying data, if available at compilation.system.newStringUninitis now considered free of side-effects allowing it to be used with--experimental:strictFuncs.std/reandstd/nreare deprecated as PCRE library is obsolete. Use https://github.com/nitely/nim-regex orstd/nre2. See: https://github.com/nim-lang/Nim/issues/23668.std/pegsnow correctly lexes UTF-8 bytes inside bare identifier-style terminals, so case-insensitive matching of non-ASCII terms (e.g.\i café) works without single-quoting.std/uri: The?operator now appends query parameters to an existing query string instead of replacing it. Fixes #19782.
Language changes
-
An experimental option
--experimental:typeBoundOpshas been added that implements the RFC https://github.com/nim-lang/RFCs/issues/380. This makes the behavior of interfaces likehash,$,==etc. more reliable for nominal types across indirect/restricted imports.# objs.nim import std/hashes type Obj* = object x*, y*: int z*: string # to be ignored for equality proc `==`*(a, b: Obj): bool = a.x == b.x and a.y == b.y proc hash*(a: Obj): Hash = $!(hash(a.x) &! hash(a.y))# main.nim {.experimental: "typeBoundOps".} from objs import Obj # objs.hash, objs.`==` not imported import std/tables var t: Table[Obj, int] t[Obj(x: 3, y: 4, z: "debug")] = 34 echo t[Obj(x: 3, y: 4, z: "ignored")] # 34See the experimental manual for more information.
Compiler changes
- Fixed a bug where
sizeof(T)inside atypedesctemplate called from a generic type'swhenclause would error with "'sizeof' requires '.importc' types to be '.completeStruct'". The issue was thathasValuelessStaticsinsemtypinst.nimdidn't recognizetyTypeDesc(tyGenericParam)as an unresolved generic parameter.
Tool changes
- Added
--rawflag when generating JSON docs to not render markup. - Added
--stdinfileflag to name of the file used when running program from stdin (defaults tostdinfile.nim) - Added
--styleCheck:warningflag to treat style check violations as warnings.
Documentation changes
- Added documentation for the
completeStructpragma in the manual.