diff --git a/docs/release-notes/.FSharp.Compiler.Service/10.0.300.md b/docs/release-notes/.FSharp.Compiler.Service/10.0.300.md index 7a8279aac29..53802cc1153 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/10.0.300.md +++ b/docs/release-notes/.FSharp.Compiler.Service/10.0.300.md @@ -25,6 +25,12 @@ * F# Scripts: Fix default reference paths resolving when an SDK directory is specified. ([PR #19270](https://github.com/dotnet/fsharp/pull/19270)) * Improve static compilation of state machines. ([PR #19297](https://github.com/dotnet/fsharp/pull/19297)) * Fix a bug where `let!` and `use!` were incorrectly allowed outside computation expressions. [PR #19347](https://github.com/dotnet/fsharp/pull/19347) +* Fix TypeLoadException when creating delegate with voidptr parameter. (Issue [#11132](https://github.com/dotnet/fsharp/issues/11132), [PR #19338](https://github.com/dotnet/fsharp/pull/19338)) +* Suppress tail calls when localloc (NativePtr.stackalloc) is used. (Issue [#13447](https://github.com/dotnet/fsharp/issues/13447), [PR #19338](https://github.com/dotnet/fsharp/pull/19338)) +* Fix TypeLoadException in Release builds with inline constraints. (Issue [#14492](https://github.com/dotnet/fsharp/issues/14492), [PR #19338](https://github.com/dotnet/fsharp/pull/19338)) +* Fix nativeptr in interfaces leads to TypeLoadException. (Issue [#14508](https://github.com/dotnet/fsharp/issues/14508), [PR #19338](https://github.com/dotnet/fsharp/pull/19338)) +* Fix box instruction for literal upcasts. (Issue [#18319](https://github.com/dotnet/fsharp/issues/18319), [PR #19338](https://github.com/dotnet/fsharp/pull/19338)) +* Fix Decimal Literal causes InvalidProgramException in Debug builds. (Issue [#18956](https://github.com/dotnet/fsharp/issues/18956), [PR #19338](https://github.com/dotnet/fsharp/pull/19338)) ### Added * FSharpType: add ImportILType ([PR #19300](https://github.com/dotnet/fsharp/pull/19300)) diff --git a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md index d2d59c319a1..7751906ad10 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md @@ -19,6 +19,13 @@ * Fixed how the source ranges of warn directives are reported (as trivia) in the parser output (by not reporting leading spaces). ([Issue #19405](https://github.com/dotnet/fsharp/issues/19405), [PR #19408]((https://github.com/dotnet/fsharp/pull/19408))) * Fix UoM value type `ToString()` returning garbage values when `--checknulls+` is enabled, caused by double address-taking in codegen. ([Issue #19435](https://github.com/dotnet/fsharp/issues/19435), [PR #19440](https://github.com/dotnet/fsharp/pull/19440)) * Fix completion inconsistently showing some obsolete members (fields and events) while hiding others (methods and properties). All obsolete members are now consistently hidden by default. ([Issue #13512](https://github.com/dotnet/fsharp/issues/13512), [PR #19506](https://github.com/dotnet/fsharp/pull/19506)) +* Fix TypeLoadException when creating delegate with voidptr parameter. (Issue [#11132](https://github.com/dotnet/fsharp/issues/11132), [PR #19338](https://github.com/dotnet/fsharp/pull/19338)) +* Suppress tail calls when localloc (NativePtr.stackalloc) is used. (Issue [#13447](https://github.com/dotnet/fsharp/issues/13447), [PR #19338](https://github.com/dotnet/fsharp/pull/19338)) +* Fix TypeLoadException in Release builds with inline constraints. (Issue [#14492](https://github.com/dotnet/fsharp/issues/14492), [PR #19338](https://github.com/dotnet/fsharp/pull/19338)) +* Fix nativeptr in interfaces leads to TypeLoadException. (Issue [#14508](https://github.com/dotnet/fsharp/issues/14508), [PR #19338](https://github.com/dotnet/fsharp/pull/19338)) +* Fix box instruction for literal upcasts. (Issue [#18319](https://github.com/dotnet/fsharp/issues/18319), [PR #19338](https://github.com/dotnet/fsharp/pull/19338)) +* Fix Decimal Literal causes InvalidProgramException in Debug builds. (Issue [#18956](https://github.com/dotnet/fsharp/issues/18956), [PR #19338](https://github.com/dotnet/fsharp/pull/19338)) +* Fix `AttributeUsage.AllowMultiple` not being inherited for attributes subclassed in C#. ([Issue #17107](https://github.com/dotnet/fsharp/issues/17107), [PR #19315](https://github.com/dotnet/fsharp/pull/19315)) ### Added diff --git a/eng/TargetFrameworks.props b/eng/TargetFrameworks.props index d384e5fbcaa..e3938d0f73a 100644 --- a/eng/TargetFrameworks.props +++ b/eng/TargetFrameworks.props @@ -11,7 +11,7 @@ - net10.0 + net11.0 $([System.Text.RegularExpressions.Regex]::Replace('$(FSharpNetCoreProductTargetFramework)', '^net(\d+)\.0$', '$1')) diff --git a/eng/Version.Details.props b/eng/Version.Details.props index 4290ffadabe..5933d2dfe98 100644 --- a/eng/Version.Details.props +++ b/eng/Version.Details.props @@ -6,7 +6,7 @@ This file should be imported by eng/Versions.props - 10.0.0-beta.26208.4 + 11.0.0-beta.26211.1 18.6.1 18.6.1 diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 286094f1cec..ed369c7e7ca 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -76,9 +76,9 @@ - + https://github.com/dotnet/arcade - ecdd5c6a7986cafabbf6a322ea09a07736a01a0d + a08169b890573cfd7f949ea9062c86a4db1aab1b https://dev.azure.com/dnceng/internal/_git/dotnet-optimization diff --git a/eng/Versions.props b/eng/Versions.props index 8248454c00f..a6a34de7ef5 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -89,7 +89,12 @@ 4.6.1 4.6.3 6.1.2 - + + 10.0.2 + $(SystemPackagesVersion) + $(SystemPackagesVersion) + $(SystemPackagesVersion) + $(SystemPackagesVersion) @@ -156,7 +161,7 @@ 5.0.0-preview.7.20364.11 5.0.0-preview.7.20364.11 - 17.14.1 + 18.0.1 2.0.2 13.0.3 3.2.2 diff --git a/eng/common/SetupNugetSources.ps1 b/eng/common/SetupNugetSources.ps1 index 65ed3a8adef..fc8d618014e 100644 --- a/eng/common/SetupNugetSources.ps1 +++ b/eng/common/SetupNugetSources.ps1 @@ -1,7 +1,6 @@ # This script adds internal feeds required to build commits that depend on internal package sources. For instance, -# dotnet6-internal would be added automatically if dotnet6 was found in the nuget.config file. Similarly, -# dotnet-eng-internal and dotnet-tools-internal are added if dotnet-eng and dotnet-tools are present. -# In addition, this script also enables disabled internal Maestro (darc-int*) feeds. +# dotnet6-internal would be added automatically if dotnet6 was found in the nuget.config file. In addition also enables +# disabled internal Maestro (darc-int*) feeds. # # Optionally, this script also adds a credential entry for each of the internal feeds if supplied. # @@ -174,16 +173,4 @@ foreach ($dotnetVersion in $dotnetVersions) { } } -# Check for dotnet-eng and add dotnet-eng-internal if present -$dotnetEngSource = $sources.SelectSingleNode("add[@key='dotnet-eng']") -if ($dotnetEngSource -ne $null) { - AddOrEnablePackageSource -Sources $sources -DisabledPackageSources $disabledSources -SourceName "dotnet-eng-internal" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet-eng-internal/nuget/$feedSuffix" -Creds $creds -Username $userName -pwd $Password -} - -# Check for dotnet-tools and add dotnet-tools-internal if present -$dotnetToolsSource = $sources.SelectSingleNode("add[@key='dotnet-tools']") -if ($dotnetToolsSource -ne $null) { - AddOrEnablePackageSource -Sources $sources -DisabledPackageSources $disabledSources -SourceName "dotnet-tools-internal" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet-tools-internal/nuget/$feedSuffix" -Creds $creds -Username $userName -pwd $Password -} - $doc.Save($filename) diff --git a/eng/common/SetupNugetSources.sh b/eng/common/SetupNugetSources.sh index b2163abbe71..b97cc536379 100755 --- a/eng/common/SetupNugetSources.sh +++ b/eng/common/SetupNugetSources.sh @@ -1,9 +1,8 @@ #!/usr/bin/env bash # This script adds internal feeds required to build commits that depend on internal package sources. For instance, -# dotnet6-internal would be added automatically if dotnet6 was found in the nuget.config file. Similarly, -# dotnet-eng-internal and dotnet-tools-internal are added if dotnet-eng and dotnet-tools are present. -# In addition, this script also enables disabled internal Maestro (darc-int*) feeds. +# dotnet6-internal would be added automatically if dotnet6 was found in the nuget.config file. In addition also enables +# disabled internal Maestro (darc-int*) feeds. # # Optionally, this script also adds a credential entry for each of the internal feeds if supplied. # @@ -174,18 +173,6 @@ for DotNetVersion in ${DotNetVersions[@]} ; do fi done -# Check for dotnet-eng and add dotnet-eng-internal if present -grep -i " /dev/null -if [ "$?" == "0" ]; then - AddOrEnablePackageSource "dotnet-eng-internal" "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet-eng-internal/nuget/$FeedSuffix" -fi - -# Check for dotnet-tools and add dotnet-tools-internal if present -grep -i " /dev/null -if [ "$?" == "0" ]; then - AddOrEnablePackageSource "dotnet-tools-internal" "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet-tools-internal/nuget/$FeedSuffix" -fi - # I want things split line by line PrevIFS=$IFS IFS=$'\n' diff --git a/eng/common/build.ps1 b/eng/common/build.ps1 index 8cfee107e7a..18397a60eb8 100644 --- a/eng/common/build.ps1 +++ b/eng/common/build.ps1 @@ -6,6 +6,7 @@ Param( [string][Alias('v')]$verbosity = "minimal", [string] $msbuildEngine = $null, [bool] $warnAsError = $true, + [string] $warnNotAsError = '', [bool] $nodeReuse = $true, [switch] $buildCheck = $false, [switch][Alias('r')]$restore, @@ -70,6 +71,7 @@ function Print-Usage() { Write-Host " -excludeCIBinarylog Don't output binary log (short: -nobl)" Write-Host " -prepareMachine Prepare machine for CI run, clean up processes after build" Write-Host " -warnAsError Sets warnaserror msbuild parameter ('true' or 'false')" + Write-Host " -warnNotAsError Sets a semi-colon delimited list of warning codes that should not be treated as errors" Write-Host " -msbuildEngine Msbuild engine to use to run build ('dotnet', 'vs', or unspecified)." Write-Host " -excludePrereleaseVS Set to exclude build engines in prerelease versions of Visual Studio" Write-Host " -nativeToolsOnMachine Sets the native tools on machine environment variable (indicating that the script should use native tools on machine)" diff --git a/eng/common/build.sh b/eng/common/build.sh index 9767bb411a4..5883e53bcfb 100755 --- a/eng/common/build.sh +++ b/eng/common/build.sh @@ -42,6 +42,7 @@ usage() echo " --prepareMachine Prepare machine for CI run, clean up processes after build" echo " --nodeReuse Sets nodereuse msbuild parameter ('true' or 'false')" echo " --warnAsError Sets warnaserror msbuild parameter ('true' or 'false')" + echo " --warnNotAsError Sets a semi-colon delimited list of warning codes that should not be treated as errors" echo " --buildCheck Sets /check msbuild parameter" echo " --fromVMR Set when building from within the VMR" echo "" @@ -78,6 +79,7 @@ ci=false clean=false warn_as_error=true +warn_not_as_error='' node_reuse=true build_check=false binary_log=false @@ -92,7 +94,7 @@ runtime_source_feed='' runtime_source_feed_key='' properties=() -while [[ $# > 0 ]]; do +while [[ $# -gt 0 ]]; do opt="$(echo "${1/#--/-}" | tr "[:upper:]" "[:lower:]")" case "$opt" in -help|-h) @@ -176,6 +178,10 @@ while [[ $# > 0 ]]; do warn_as_error=$2 shift ;; + -warnnotaserror) + warn_not_as_error=$2 + shift + ;; -nodereuse) node_reuse=$2 shift diff --git a/eng/common/core-templates/job/job.yml b/eng/common/core-templates/job/job.yml index eaed6d87e65..66c7988f222 100644 --- a/eng/common/core-templates/job/job.yml +++ b/eng/common/core-templates/job/job.yml @@ -19,6 +19,8 @@ parameters: # publishing defaults artifacts: '' enableMicrobuild: false + enablePreviewMicrobuild: false + microbuildPluginVersion: 'latest' enableMicrobuildForMacAndLinux: false microbuildUseESRP: true enablePublishBuildArtifacts: false @@ -71,6 +73,8 @@ jobs: templateContext: ${{ parameters.templateContext }} variables: + - name: AllowPtrToDetectTestRunRetryFiles + value: true - ${{ if ne(parameters.enableTelemetry, 'false') }}: - name: DOTNET_CLI_TELEMETRY_PROFILE value: '$(Build.Repository.Uri)' @@ -128,6 +132,8 @@ jobs: - template: /eng/common/core-templates/steps/install-microbuild.yml parameters: enableMicrobuild: ${{ parameters.enableMicrobuild }} + enablePreviewMicrobuild: ${{ parameters.enablePreviewMicrobuild }} + microbuildPluginVersion: ${{ parameters.microbuildPluginVersion }} enableMicrobuildForMacAndLinux: ${{ parameters.enableMicrobuildForMacAndLinux }} microbuildUseESRP: ${{ parameters.microbuildUseESRP }} continueOnError: ${{ parameters.continueOnError }} @@ -150,6 +156,8 @@ jobs: - template: /eng/common/core-templates/steps/cleanup-microbuild.yml parameters: enableMicrobuild: ${{ parameters.enableMicrobuild }} + enablePreviewMicrobuild: ${{ parameters.enablePreviewMicrobuild }} + microbuildPluginVersion: ${{ parameters.microbuildPluginVersion }} enableMicrobuildForMacAndLinux: ${{ parameters.enableMicrobuildForMacAndLinux }} continueOnError: ${{ parameters.continueOnError }} diff --git a/eng/common/core-templates/job/publish-build-assets.yml b/eng/common/core-templates/job/publish-build-assets.yml index 06f2eed0323..700f7711465 100644 --- a/eng/common/core-templates/job/publish-build-assets.yml +++ b/eng/common/core-templates/job/publish-build-assets.yml @@ -91,8 +91,8 @@ jobs: fetchDepth: 3 clean: true - - ${{ if eq(parameters.isAssetlessBuild, 'false') }}: - - ${{ if eq(parameters.publishingVersion, 3) }}: + - ${{ if eq(parameters.isAssetlessBuild, 'false') }}: + - ${{ if eq(parameters.publishingVersion, 3) }}: - task: DownloadPipelineArtifact@2 displayName: Download Asset Manifests inputs: @@ -117,7 +117,7 @@ jobs: flattenFolders: true condition: ${{ parameters.condition }} continueOnError: ${{ parameters.continueOnError }} - + - task: NuGetAuthenticate@1 # Populate internal runtime variables. @@ -125,7 +125,7 @@ jobs: ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: parameters: legacyCredential: $(dn-bot-dnceng-artifact-feeds-rw) - + - template: /eng/common/templates/steps/enable-internal-runtimes.yml - task: AzureCLI@2 @@ -145,7 +145,7 @@ jobs: condition: ${{ parameters.condition }} continueOnError: ${{ parameters.continueOnError }} - + - task: powershell@2 displayName: Create ReleaseConfigs Artifact inputs: @@ -191,7 +191,7 @@ jobs: BARBuildId: ${{ parameters.BARBuildId }} PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} is1ESPipeline: ${{ parameters.is1ESPipeline }} - + # Darc is targeting 8.0, so make sure it's installed - task: UseDotNet@2 inputs: diff --git a/eng/common/core-templates/job/renovate.yml b/eng/common/core-templates/job/renovate.yml new file mode 100644 index 00000000000..ff86c80b468 --- /dev/null +++ b/eng/common/core-templates/job/renovate.yml @@ -0,0 +1,196 @@ +# -------------------------------------------------------------------------------------- +# Renovate Bot Job Template +# -------------------------------------------------------------------------------------- +# This Azure DevOps pipeline job template runs Renovate (https://docs.renovatebot.com/) +# to automatically update dependencies in a GitHub repository. +# +# Renovate scans the repository for dependency files and creates pull requests to update +# outdated dependencies based on the configuration specified in the renovateConfigPath +# parameter. +# +# Usage: +# For each product repo wanting to make use of Renovate, this template is called from +# an internal Azure DevOps pipeline, typically with a schedule trigger, to check for +# and propose dependency updates. +# +# For more info, see https://github.com/dotnet/arcade/blob/main/Documentation/Renovate.md +# -------------------------------------------------------------------------------------- + +parameters: + +# Path to the Renovate configuration file within the repository. +- name: renovateConfigPath + type: string + default: 'eng/renovate.json' + +# GitHub repository to run Renovate against, in the format 'owner/repo'. +# This could technically be any repo but convention is to target the same +# repo that contains the calling pipeline. The Renovate config file would +# be co-located with the pipeline's repo and, in most cases, the config +# file is specific to the repo being targeted. +- name: gitHubRepo + type: string + +# List of base branches to target for Renovate PRs. +# NOTE: The Renovate configuration file is always read from the branch where the +# pipeline is run, NOT from the target branches specified here. If you need different +# configurations for different branches, run the pipeline from each branch separately. +- name: baseBranches + type: object + default: + - main + +# When true, Renovate will run in dry run mode, which previews changes without creating PRs. +# See the 'Run Renovate' step log output for details of what would have been changed. +- name: dryRun + type: boolean + default: false + +# By default, Renovate will not recreate a PR for a given dependency/version pair that was +# previously closed. This allows opting in to always recreating PRs even if they were +# previously closed. +- name: forceRecreatePR + type: boolean + default: false + +# Name of the arcade repository resource in the pipeline. +# This allows repos which haven't been onboarded to Arcade to still use this +# template by checking out the repo as a resource with a custom name and pointing +# this parameter to it. +- name: arcadeRepoResource + type: string + default: self + +# Directory name for the self repo under $(Build.SourcesDirectory) in multi-checkout. +# In multi-checkout (when arcadeRepoResource != 'self'), Azure DevOps checks out the +# self repo to $(Build.SourcesDirectory)/. Set this to match the auto-generated +# directory name. Using the auto-generated name is necessary rather than explicitly +# defining a checkout path because container jobs expect repos to live under the agent's +# workspace ($(Pipeline.Workspace)). On some self-hosted setups the host path +# (e.g., /mnt/vss/_work) differs from the container path (e.g., /__w), and a custom checkout +# path can fail validation. Using the default checkout location keeps the paths consistent +# and avoids this issue. +- name: selfRepoName + type: string + default: '' +- name: arcadeRepoName + type: string + default: '' + +# Pool configuration for the job. +- name: pool + type: object + default: + name: NetCore1ESPool-Internal + image: build.azurelinux.3.amd64 + os: linux + +jobs: +- job: Renovate + displayName: Run Renovate + container: RenovateContainer + variables: + - group: dotnet-renovate-bot + # The Renovate version is automatically updated by https://github.com/dotnet/arcade/blob/main/azure-pipelines-renovate.yml. + # Changing the variable name here would require updating the name in https://github.com/dotnet/arcade/blob/main/eng/renovate.json as well. + - name: renovateVersion + value: '42' + readonly: true + - name: renovateLogFilePath + value: '$(Build.ArtifactStagingDirectory)/renovate.json' + readonly: true + - name: dryRunArg + readonly: true + ${{ if eq(parameters.dryRun, true) }}: + value: 'full' + ${{ else }}: + value: '' + - name: recreateWhenArg + readonly: true + ${{ if eq(parameters.forceRecreatePR, true) }}: + value: 'always' + ${{ else }}: + value: '' + # In multi-checkout (without custom paths), Azure DevOps places each repo under + # $(Build.SourcesDirectory)/. selfRepoName must be provided in that case. + - name: selfRepoPath + readonly: true + ${{ if eq(parameters.arcadeRepoResource, 'self') }}: + value: '$(Build.SourcesDirectory)' + ${{ else }}: + value: '$(Build.SourcesDirectory)/${{ parameters.selfRepoName }}' + - name: arcadeRepoPath + readonly: true + ${{ if eq(parameters.arcadeRepoResource, 'self') }}: + value: '$(Build.SourcesDirectory)' + ${{ else }}: + value: '$(Build.SourcesDirectory)/${{ parameters.arcadeRepoName }}' + pool: ${{ parameters.pool }} + + templateContext: + outputParentDirectory: $(Build.ArtifactStagingDirectory) + outputs: + - output: pipelineArtifact + displayName: Publish Renovate Log + condition: succeededOrFailed() + targetPath: $(Build.ArtifactStagingDirectory) + artifactName: $(Agent.JobName)_Logs_Attempt$(System.JobAttempt) + isProduction: false # logs are non-production artifacts + + steps: + - checkout: self + fetchDepth: 1 + + - ${{ if ne(parameters.arcadeRepoResource, 'self') }}: + - checkout: ${{ parameters.arcadeRepoResource }} + fetchDepth: 1 + + - script: | + renovate-config-validator $(selfRepoPath)/${{parameters.renovateConfigPath}} 2>&1 | tee /tmp/renovate-config-validator.out + validatorExit=${PIPESTATUS[0]} + if grep -q '^ WARN:' /tmp/renovate-config-validator.out; then + echo "##vso[task.logissue type=warning]Renovate config validator produced warnings." + echo "##vso[task.complete result=SucceededWithIssues]" + fi + exit $validatorExit + displayName: Validate Renovate config + env: + LOG_LEVEL: info + LOG_FILE_LEVEL: debug + LOG_FILE: $(Build.ArtifactStagingDirectory)/renovate-config-validator.json + + - script: | + . $(arcadeRepoPath)/eng/common/renovate.env + renovate 2>&1 | tee /tmp/renovate.out + renovateExit=${PIPESTATUS[0]} + if grep -q '^ WARN:' /tmp/renovate.out; then + echo "##vso[task.logissue type=warning]Renovate produced warnings." + echo "##vso[task.complete result=SucceededWithIssues]" + fi + exit $renovateExit + displayName: Run Renovate + env: + RENOVATE_FORK_TOKEN: $(BotAccount-dotnet-renovate-bot-PAT) + RENOVATE_TOKEN: $(BotAccount-dotnet-renovate-bot-PAT) + RENOVATE_REPOSITORIES: ${{parameters.gitHubRepo}} + RENOVATE_BASE_BRANCHES: ${{ convertToJson(parameters.baseBranches) }} + RENOVATE_DRY_RUN: $(dryRunArg) + RENOVATE_RECREATE_WHEN: $(recreateWhenArg) + LOG_LEVEL: info + LOG_FILE_LEVEL: debug + LOG_FILE: $(renovateLogFilePath) + RENOVATE_CONFIG_FILE: $(selfRepoPath)/${{parameters.renovateConfigPath}} + + - script: | + echo "PRs created by Renovate:" + if [ -s "$(renovateLogFilePath)" ]; then + if ! jq -r 'select(.msg == "PR created" and .pr != null) | "https://github.com/\(.repository)/pull/\(.pr)"' "$(renovateLogFilePath)" | sort -u; then + echo "##vso[task.logissue type=warning]Failed to parse Renovate log file with jq." + echo "##vso[task.complete result=SucceededWithIssues]" + fi + else + echo "##vso[task.logissue type=warning]No Renovate log file found or file is empty." + echo "##vso[task.complete result=SucceededWithIssues]" + fi + displayName: List created PRs + condition: and(succeededOrFailed(), eq('${{ parameters.dryRun }}', false)) diff --git a/eng/common/core-templates/job/source-index-stage1.yml b/eng/common/core-templates/job/source-index-stage1.yml index 76baf5c2725..bac6ac5faac 100644 --- a/eng/common/core-templates/job/source-index-stage1.yml +++ b/eng/common/core-templates/job/source-index-stage1.yml @@ -15,6 +15,8 @@ jobs: variables: - name: BinlogPath value: ${{ parameters.binlogPath }} + - name: skipComponentGovernanceDetection + value: true - template: /eng/common/core-templates/variables/pool-providers.yml parameters: is1ESPipeline: ${{ parameters.is1ESPipeline }} @@ -25,10 +27,10 @@ jobs: pool: ${{ if eq(variables['System.TeamProject'], 'public') }}: name: $(DncEngPublicBuildPool) - image: windows.vs2026preview.scout.amd64.open + image: windows.vs2026.amd64.open ${{ if eq(variables['System.TeamProject'], 'internal') }}: name: $(DncEngInternalBuildPool) - image: windows.vs2026preview.scout.amd64 + image: windows.vs2026.amd64 steps: - ${{ if eq(parameters.is1ESPipeline, '') }}: diff --git a/eng/common/core-templates/post-build/common-variables.yml b/eng/common/core-templates/post-build/common-variables.yml index d5627a994ae..db298ae16ba 100644 --- a/eng/common/core-templates/post-build/common-variables.yml +++ b/eng/common/core-templates/post-build/common-variables.yml @@ -11,8 +11,6 @@ variables: - name: MaestroApiVersion value: "2020-02-20" - - name: SourceLinkCLIVersion - value: 3.0.0 - name: SymbolToolVersion value: 1.0.1 - name: BinlogToolVersion diff --git a/eng/common/core-templates/post-build/post-build.yml b/eng/common/core-templates/post-build/post-build.yml index 905a6315e2d..fcf40d1d2e6 100644 --- a/eng/common/core-templates/post-build/post-build.yml +++ b/eng/common/core-templates/post-build/post-build.yml @@ -1,118 +1,108 @@ parameters: - # Which publishing infra should be used. THIS SHOULD MATCH THE VERSION ON THE BUILD MANIFEST. - # Publishing V1 is no longer supported - # Publishing V2 is no longer supported - # Publishing V3 is the default - - name: publishingInfraVersion - displayName: Which version of publishing should be used to promote the build definition? - type: number - default: 3 - values: - - 3 - - 4 - - - name: BARBuildId - displayName: BAR Build Id - type: number - default: 0 - - - name: PromoteToChannelIds - displayName: Channel to promote BARBuildId to - type: string - default: '' - - - name: enableSourceLinkValidation - displayName: Enable SourceLink validation - type: boolean - default: false - - - name: enableSigningValidation - displayName: Enable signing validation - type: boolean - default: true - - - name: enableSymbolValidation - displayName: Enable symbol validation - type: boolean - default: false - - - name: enableNugetValidation - displayName: Enable NuGet validation - type: boolean - default: true - - - name: publishInstallersAndChecksums - displayName: Publish installers and checksums - type: boolean - default: true - - - name: requireDefaultChannels - displayName: Fail the build if there are no default channel(s) registrations for the current build - type: boolean - default: false - - - name: SDLValidationParameters - type: object - default: - enable: false - publishGdn: false - continueOnError: false - params: '' - artifactNames: '' - downloadArtifacts: true - - - name: isAssetlessBuild - type: boolean - displayName: Is Assetless Build - default: false - - # These parameters let the user customize the call to sdk-task.ps1 for publishing - # symbols & general artifacts as well as for signing validation - - name: symbolPublishingAdditionalParameters - displayName: Symbol publishing additional parameters - type: string - default: '' - - - name: artifactsPublishingAdditionalParameters - displayName: Artifact publishing additional parameters - type: string - default: '' - - - name: signingValidationAdditionalParameters - displayName: Signing validation additional parameters - type: string - default: '' - - # Which stages should finish execution before post-build stages start - - name: validateDependsOn - type: object - default: - - build - - - name: publishDependsOn - type: object - default: - - Validate - - # Optional: Call asset publishing rather than running in a separate stage - - name: publishAssetsImmediately - type: boolean - default: false - - - name: is1ESPipeline - type: boolean - default: false +# Which publishing infra should be used. THIS SHOULD MATCH THE VERSION ON THE BUILD MANIFEST. +# Publishing V1 is no longer supported +# Publishing V2 is no longer supported +# Publishing V3 is the default +- name: publishingInfraVersion + displayName: Which version of publishing should be used to promote the build definition? + type: number + default: 3 + values: + - 3 + - 4 + +- name: BARBuildId + displayName: BAR Build Id + type: number + default: 0 + +- name: PromoteToChannelIds + displayName: Channel to promote BARBuildId to + type: string + default: '' + +- name: enableSourceLinkValidation + displayName: Enable SourceLink validation + type: boolean + default: false + +- name: enableSigningValidation + displayName: Enable signing validation + type: boolean + default: true + +- name: enableSymbolValidation + displayName: Enable symbol validation + type: boolean + default: false + +- name: enableNugetValidation + displayName: Enable NuGet validation + type: boolean + default: true + +- name: publishInstallersAndChecksums + displayName: Publish installers and checksums + type: boolean + default: true + +- name: requireDefaultChannels + displayName: Fail the build if there are no default channel(s) registrations for the current build + type: boolean + default: false + +- name: isAssetlessBuild + type: boolean + displayName: Is Assetless Build + default: false + +# These parameters let the user customize the call to sdk-task.ps1 for publishing +# symbols & general artifacts as well as for signing validation +- name: symbolPublishingAdditionalParameters + displayName: Symbol publishing additional parameters + type: string + default: '' + +- name: artifactsPublishingAdditionalParameters + displayName: Artifact publishing additional parameters + type: string + default: '' + +- name: signingValidationAdditionalParameters + displayName: Signing validation additional parameters + type: string + default: '' + +# Which stages should finish execution before post-build stages start +- name: validateDependsOn + type: object + default: + - build + +- name: publishDependsOn + type: object + default: + - Validate + +# Optional: Call asset publishing rather than running in a separate stage +- name: publishAssetsImmediately + type: boolean + default: false + +- name: is1ESPipeline + type: boolean + default: false stages: -- ${{ if or(eq( parameters.enableNugetValidation, 'true'), eq(parameters.enableSigningValidation, 'true'), eq(parameters.enableSourceLinkValidation, 'true'), eq(parameters.SDLValidationParameters.enable, 'true')) }}: +- ${{ if or(eq( parameters.enableNugetValidation, 'true'), eq(parameters.enableSigningValidation, 'true'), eq(parameters.enableSourceLinkValidation, 'true')) }}: - stage: Validate dependsOn: ${{ parameters.validateDependsOn }} displayName: Validate Build Assets variables: - - template: /eng/common/core-templates/post-build/common-variables.yml - - template: /eng/common/core-templates/variables/pool-providers.yml - parameters: - is1ESPipeline: ${{ parameters.is1ESPipeline }} + - template: /eng/common/core-templates/post-build/common-variables.yml + - template: /eng/common/core-templates/variables/pool-providers.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} jobs: - job: displayName: NuGet Validation @@ -128,49 +118,49 @@ stages: ${{ else }}: ${{ if eq(parameters.is1ESPipeline, true) }}: name: $(DncEngInternalBuildPool) - image: windows.vs2026preview.scout.amd64 + image: windows.vs2026.amd64 os: windows ${{ else }}: name: $(DncEngInternalBuildPool) - demands: ImageOverride -equals windows.vs2026preview.scout.amd64 + demands: ImageOverride -equals windows.vs2026.amd64 steps: - - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - is1ESPipeline: ${{ parameters.is1ESPipeline }} - - - ${{ if ne(parameters.publishingInfraVersion, 4) }}: - - task: DownloadBuildArtifacts@0 - displayName: Download Package Artifacts - inputs: - buildType: specific - buildVersionToDownload: specific - project: $(AzDOProjectName) - pipeline: $(AzDOPipelineId) - buildId: $(AzDOBuildId) - artifactName: PackageArtifacts - checkDownloadedFiles: true - - ${{ if eq(parameters.publishingInfraVersion, 4) }}: - - task: DownloadPipelineArtifact@2 - displayName: Download Pipeline Artifacts (V4) - inputs: - itemPattern: '*/packages/**/*.nupkg' - targetPath: '$(Build.ArtifactStagingDirectory)/PipelineArtifactsDownload' - - task: CopyFiles@2 - displayName: Flatten packages to PackageArtifacts - inputs: - SourceFolder: '$(Build.ArtifactStagingDirectory)/PipelineArtifactsDownload' - Contents: '**/*.nupkg' - TargetFolder: '$(Build.ArtifactStagingDirectory)/PackageArtifacts' - flattenFolders: true - - - task: PowerShell@2 - displayName: Validate + - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + BARBuildId: ${{ parameters.BARBuildId }} + PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} + is1ESPipeline: ${{ parameters.is1ESPipeline }} + + - ${{ if ne(parameters.publishingInfraVersion, 4) }}: + - task: DownloadBuildArtifacts@0 + displayName: Download Package Artifacts + inputs: + buildType: specific + buildVersionToDownload: specific + project: $(AzDOProjectName) + pipeline: $(AzDOPipelineId) + buildId: $(AzDOBuildId) + artifactName: PackageArtifacts + checkDownloadedFiles: true + - ${{ if eq(parameters.publishingInfraVersion, 4) }}: + - task: DownloadPipelineArtifact@2 + displayName: Download Pipeline Artifacts (V4) + inputs: + itemPattern: '*/packages/**/*.nupkg' + targetPath: '$(Build.ArtifactStagingDirectory)/PipelineArtifactsDownload' + - task: CopyFiles@2 + displayName: Flatten packages to PackageArtifacts inputs: - filePath: $(System.DefaultWorkingDirectory)/eng/common/post-build/nuget-validation.ps1 - arguments: -PackagesPath $(Build.ArtifactStagingDirectory)/PackageArtifacts/ + SourceFolder: '$(Build.ArtifactStagingDirectory)/PipelineArtifactsDownload' + Contents: '**/*.nupkg' + TargetFolder: '$(Build.ArtifactStagingDirectory)/PackageArtifacts' + flattenFolders: true + + - task: PowerShell@2 + displayName: Validate + inputs: + filePath: $(System.DefaultWorkingDirectory)/eng/common/post-build/nuget-validation.ps1 + arguments: -PackagesPath $(Build.ArtifactStagingDirectory)/PackageArtifacts/ - job: displayName: Signing Validation @@ -184,143 +174,96 @@ stages: os: windows # If it's not devdiv, it's dnceng ${{ else }}: - ${{ if eq(parameters.is1ESPipeline, true) }}: + ${{ if eq(parameters.is1ESPipeline, true) }}: name: $(DncEngInternalBuildPool) image: windows.vs2026.amd64 os: windows ${{ else }}: name: $(DncEngInternalBuildPool) - demands: ImageOverride -equals windows.vs2026preview.scout.amd64 + demands: ImageOverride -equals windows.vs2026.amd64 steps: - - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - is1ESPipeline: ${{ parameters.is1ESPipeline }} - - - ${{ if ne(parameters.publishingInfraVersion, 4) }}: - - task: DownloadBuildArtifacts@0 - displayName: Download Package Artifacts - inputs: - buildType: specific - buildVersionToDownload: specific - project: $(AzDOProjectName) - pipeline: $(AzDOPipelineId) - buildId: $(AzDOBuildId) - artifactName: PackageArtifacts - checkDownloadedFiles: true - - ${{ if eq(parameters.publishingInfraVersion, 4) }}: - - task: DownloadPipelineArtifact@2 - displayName: Download Pipeline Artifacts (V4) - inputs: - itemPattern: '*/packages/**/*.nupkg' - targetPath: '$(Build.ArtifactStagingDirectory)/PipelineArtifactsDownload' - - task: CopyFiles@2 - displayName: Flatten packages to PackageArtifacts - inputs: - SourceFolder: '$(Build.ArtifactStagingDirectory)/PipelineArtifactsDownload' - Contents: '**/*.nupkg' - TargetFolder: '$(Build.ArtifactStagingDirectory)/PackageArtifacts' - flattenFolders: true - - # This is necessary whenever we want to publish/restore to an AzDO private feed - # Since sdk-task.ps1 tries to restore packages we need to do this authentication here - # otherwise it'll complain about accessing a private feed. - - task: NuGetAuthenticate@1 - displayName: 'Authenticate to AzDO Feeds' - - # Signing validation will optionally work with the buildmanifest file which is downloaded from - # Azure DevOps above. - - task: PowerShell@2 - displayName: Validate - inputs: - filePath: eng\common\sdk-task.ps1 - arguments: -task SigningValidation -restore -msbuildEngine vs - /p:PackageBasePath='$(Build.ArtifactStagingDirectory)/PackageArtifacts' - /p:SignCheckExclusionsFile='$(System.DefaultWorkingDirectory)/eng/SignCheckExclusionsFile.txt' - ${{ parameters.signingValidationAdditionalParameters }} - - - template: /eng/common/core-templates/steps/publish-logs.yml - parameters: - is1ESPipeline: ${{ parameters.is1ESPipeline }} - StageLabel: 'Validation' - JobLabel: 'Signing' - BinlogToolVersion: $(BinlogToolVersion) + - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + BARBuildId: ${{ parameters.BARBuildId }} + PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} + is1ESPipeline: ${{ parameters.is1ESPipeline }} - - job: - displayName: SourceLink Validation - condition: eq( ${{ parameters.enableSourceLinkValidation }}, 'true') - pool: - # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - name: AzurePipelines-EO - image: 1ESPT-Windows2025 - demands: Cmd - os: windows - # If it's not devdiv, it's dnceng - ${{ else }}: - ${{ if eq(parameters.is1ESPipeline, true) }}: - name: $(DncEngInternalBuildPool) - image: windows.vs2026.amd64 - os: windows - ${{ else }}: - name: $(DncEngInternalBuildPool) - demands: ImageOverride -equals windows.vs2026preview.scout.amd64 - steps: - - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - is1ESPipeline: ${{ parameters.is1ESPipeline }} - - - ${{ if ne(parameters.publishingInfraVersion, 4) }}: - - task: DownloadBuildArtifacts@0 - displayName: Download Blob Artifacts - inputs: - buildType: specific - buildVersionToDownload: specific - project: $(AzDOProjectName) - pipeline: $(AzDOPipelineId) - buildId: $(AzDOBuildId) - artifactName: BlobArtifacts - checkDownloadedFiles: true - - ${{ if eq(parameters.publishingInfraVersion, 4) }}: - - task: DownloadPipelineArtifact@2 - displayName: Download Pipeline Artifacts (V4) - inputs: - itemPattern: '*/assets/**' - targetPath: '$(Build.ArtifactStagingDirectory)/PipelineArtifactsDownload' - - task: CopyFiles@2 - displayName: Flatten assets to BlobArtifacts - inputs: - SourceFolder: '$(Build.ArtifactStagingDirectory)/PipelineArtifactsDownload' - Contents: '**/*' - TargetFolder: '$(Build.ArtifactStagingDirectory)/BlobArtifacts' - flattenFolders: true - - - task: PowerShell@2 - displayName: Validate + - ${{ if ne(parameters.publishingInfraVersion, 4) }}: + - task: DownloadBuildArtifacts@0 + displayName: Download Package Artifacts + inputs: + buildType: specific + buildVersionToDownload: specific + project: $(AzDOProjectName) + pipeline: $(AzDOPipelineId) + buildId: $(AzDOBuildId) + artifactName: PackageArtifacts + checkDownloadedFiles: true + - ${{ if eq(parameters.publishingInfraVersion, 4) }}: + - task: DownloadPipelineArtifact@2 + displayName: Download Pipeline Artifacts (V4) + inputs: + itemPattern: '*/packages/**/*.nupkg' + targetPath: '$(Build.ArtifactStagingDirectory)/PipelineArtifactsDownload' + - task: CopyFiles@2 + displayName: Flatten packages to PackageArtifacts + inputs: + SourceFolder: '$(Build.ArtifactStagingDirectory)/PipelineArtifactsDownload' + Contents: '**/*.nupkg' + TargetFolder: '$(Build.ArtifactStagingDirectory)/PackageArtifacts' + flattenFolders: true + + # This is necessary whenever we want to publish/restore to an AzDO private feed + # Since sdk-task.ps1 tries to restore packages we need to do this authentication here + # otherwise it'll complain about accessing a private feed. + - task: NuGetAuthenticate@1 + displayName: 'Authenticate to AzDO Feeds' + + # Signing validation will optionally work with the buildmanifest file which is downloaded from + # Azure DevOps above. + - task: PowerShell@2 + displayName: Validate + inputs: + filePath: eng\common\sdk-task.ps1 + arguments: -task SigningValidation -restore + /p:PackageBasePath='$(Build.ArtifactStagingDirectory)/PackageArtifacts' + /p:SignCheckExclusionsFile='$(System.DefaultWorkingDirectory)/eng/SignCheckExclusionsFile.txt' + ${{ parameters.signingValidationAdditionalParameters }} + + - template: /eng/common/core-templates/steps/publish-logs.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} + StageLabel: 'Validation' + JobLabel: 'Signing' + BinlogToolVersion: $(BinlogToolVersion) + + # SourceLink validation has been removed — the underlying CLI tool + # (targeting netcoreapp2.1) has not functioned for years. + # The enableSourceLinkValidation parameter is kept but ignored so + # existing pipelines that pass it are not broken. + # See https://github.com/dotnet/arcade/issues/16647 + - ${{ if eq(parameters.enableSourceLinkValidation, 'true') }}: + - job: + displayName: 'SourceLink Validation Removed - please remove enableSourceLinkValidation from your pipeline' + pool: server + steps: + - task: Delay@1 + displayName: 'Warning: SourceLink validation removed (see https://github.com/dotnet/arcade/issues/16647)' inputs: - filePath: $(System.DefaultWorkingDirectory)/eng/common/post-build/sourcelink-validation.ps1 - arguments: -InputPath $(Build.ArtifactStagingDirectory)/BlobArtifacts/ - -ExtractPath $(Agent.BuildDirectory)/Extract/ - -GHRepoName $(Build.Repository.Name) - -GHCommit $(Build.SourceVersion) - -SourcelinkCliVersion $(SourceLinkCLIVersion) - continueOnError: true + delayForMinutes: '0' - ${{ if ne(parameters.publishAssetsImmediately, 'true') }}: - stage: publish_using_darc - ${{ if or(eq(parameters.enableNugetValidation, 'true'), eq(parameters.enableSigningValidation, 'true'), eq(parameters.enableSourceLinkValidation, 'true'), eq(parameters.SDLValidationParameters.enable, 'true')) }}: + ${{ if or(eq(parameters.enableNugetValidation, 'true'), eq(parameters.enableSigningValidation, 'true'), eq(parameters.enableSourceLinkValidation, 'true')) }}: dependsOn: ${{ parameters.publishDependsOn }} ${{ else }}: dependsOn: ${{ parameters.validateDependsOn }} displayName: Publish using Darc variables: - - template: /eng/common/core-templates/post-build/common-variables.yml - - template: /eng/common/core-templates/variables/pool-providers.yml - parameters: - is1ESPipeline: ${{ parameters.is1ESPipeline }} + - template: /eng/common/core-templates/post-build/common-variables.yml + - template: /eng/common/core-templates/variables/pool-providers.yml + parameters: + is1ESPipeline: ${{ parameters.is1ESPipeline }} jobs: - job: displayName: Publish Using Darc @@ -334,7 +277,7 @@ stages: os: windows # If it's not devdiv, it's dnceng ${{ else }}: - ${{ if eq(parameters.is1ESPipeline, true) }}: + ${{ if eq(parameters.is1ESPipeline, true) }}: name: NetCore1ESPool-Publishing-Internal image: windows.vs2026.amd64 os: windows @@ -342,34 +285,33 @@ stages: name: NetCore1ESPool-Publishing-Internal demands: ImageOverride -equals windows.vs2026.amd64 steps: - - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - is1ESPipeline: ${{ parameters.is1ESPipeline }} - - - task: NuGetAuthenticate@1 - - # Populate internal runtime variables. - - template: /eng/common/templates/steps/enable-internal-sources.yml - parameters: - legacyCredential: $(dn-bot-dnceng-artifact-feeds-rw) - - - template: /eng/common/templates/steps/enable-internal-runtimes.yml + - template: /eng/common/core-templates/post-build/setup-maestro-vars.yml + parameters: + BARBuildId: ${{ parameters.BARBuildId }} + PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} + is1ESPipeline: ${{ parameters.is1ESPipeline }} - # Darc is targeting 8.0, so make sure it's installed - - task: UseDotNet@2 - inputs: - version: 8.0.x + - task: NuGetAuthenticate@1 - - task: AzureCLI@2 - displayName: Publish Using Darc - inputs: - azureSubscription: "Darc: Maestro Production" - scriptType: ps - scriptLocation: scriptPath - scriptPath: $(System.DefaultWorkingDirectory)/eng/common/post-build/publish-using-darc.ps1 - arguments: > + # Populate internal runtime variables. + - template: /eng/common/templates/steps/enable-internal-sources.yml + parameters: + legacyCredential: $(dn-bot-dnceng-artifact-feeds-rw) + + - template: /eng/common/templates/steps/enable-internal-runtimes.yml + + - task: UseDotNet@2 + inputs: + version: 8.0.x + + - task: AzureCLI@2 + displayName: Publish Using Darc + inputs: + azureSubscription: "Darc: Maestro Production" + scriptType: ps + scriptLocation: scriptPath + scriptPath: $(System.DefaultWorkingDirectory)/eng/common/post-build/publish-using-darc.ps1 + arguments: > -BuildId $(BARBuildId) -PublishingInfraVersion 3 -AzdoToken '$(System.AccessToken)' diff --git a/eng/common/core-templates/stages/renovate.yml b/eng/common/core-templates/stages/renovate.yml new file mode 100644 index 00000000000..edab2818258 --- /dev/null +++ b/eng/common/core-templates/stages/renovate.yml @@ -0,0 +1,111 @@ +# -------------------------------------------------------------------------------------- +# Renovate Pipeline Template +# -------------------------------------------------------------------------------------- +# This template provides a complete reusable pipeline definition for running Renovate +# in a 1ES Official pipeline. Pipelines can extend from this template and only need +# to pass the Renovate job parameters. +# +# For more info, see https://github.com/dotnet/arcade/blob/main/Documentation/Renovate.md +# -------------------------------------------------------------------------------------- + +parameters: + +# Path to the Renovate configuration file within the repository. +- name: renovateConfigPath + type: string + default: 'eng/renovate.json' + +# GitHub repository to run Renovate against, in the format 'owner/repo'. +- name: gitHubRepo + type: string + +# List of base branches to target for Renovate PRs. +- name: baseBranches + type: object + default: + - main + +# When true, Renovate will run in dry run mode. +- name: dryRun + type: boolean + default: false + +# When true, Renovate will recreate PRs even if they were previously closed. +- name: forceRecreatePR + type: boolean + default: false + +# Name of the arcade repository resource in the pipeline. +# This allows repos which haven't been onboarded to Arcade to still use this +# template by checking out the repo as a resource with a custom name and pointing +# this parameter to it. +- name: arcadeRepoResource + type: string + default: 'self' + +- name: selfRepoName + type: string + default: '' +- name: arcadeRepoName + type: string + default: '' + +# Pool configuration for the pipeline. +- name: pool + type: object + default: + name: NetCore1ESPool-Internal + image: build.azurelinux.3.amd64 + os: linux + +# Renovate version used in the container image tag. +- name: renovateVersion + default: 43 + type: number + +# Pool configuration for SDL analysis. +- name: sdlPool + type: object + default: + name: NetCore1ESPool-Internal + image: windows.vs2026.amd64 + os: windows + +resources: + repositories: + - repository: 1ESPipelineTemplates + type: git + name: 1ESPipelineTemplates/1ESPipelineTemplates + ref: refs/tags/release + +extends: + template: v1/1ES.Official.PipelineTemplate.yml@1ESPipelineTemplates + parameters: + pool: ${{ parameters.pool }} + sdl: + sourceAnalysisPool: ${{ parameters.sdlPool }} + # When repos that aren't onboarded to Arcade use this template, they set the + # arcadeRepoResource parameter to point to their Arcade repo resource. In that case, + # Aracde will be excluded from SDL analysis. + ${{ if ne(parameters.arcadeRepoResource, 'self') }}: + sourceRepositoriesToScan: + exclude: + - repository: ${{ parameters.arcadeRepoResource }} + containers: + RenovateContainer: + image: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-renovate-${{ parameters.renovateVersion }}-amd64 + stages: + - stage: Renovate + displayName: Run Renovate + jobs: + - template: /eng/common/core-templates/job/renovate.yml@${{ parameters.arcadeRepoResource }} + parameters: + renovateConfigPath: ${{ parameters.renovateConfigPath }} + gitHubRepo: ${{ parameters.gitHubRepo }} + baseBranches: ${{ parameters.baseBranches }} + dryRun: ${{ parameters.dryRun }} + forceRecreatePR: ${{ parameters.forceRecreatePR }} + pool: ${{ parameters.pool }} + arcadeRepoResource: ${{ parameters.arcadeRepoResource }} + selfRepoName: ${{ parameters.selfRepoName }} + arcadeRepoName: ${{ parameters.arcadeRepoName }} diff --git a/eng/common/core-templates/steps/install-microbuild-impl.yml b/eng/common/core-templates/steps/install-microbuild-impl.yml new file mode 100644 index 00000000000..da22beb3f60 --- /dev/null +++ b/eng/common/core-templates/steps/install-microbuild-impl.yml @@ -0,0 +1,34 @@ +parameters: + - name: microbuildTaskInputs + type: object + default: {} + + - name: microbuildEnv + type: object + default: {} + + - name: enablePreviewMicrobuild + type: boolean + default: false + + - name: condition + type: string + + - name: continueOnError + type: boolean + +steps: +- ${{ if eq(parameters.enablePreviewMicrobuild, true) }}: + - task: MicroBuildSigningPluginPreview@4 + displayName: Install Preview MicroBuild plugin + inputs: ${{ parameters.microbuildTaskInputs }} + env: ${{ parameters.microbuildEnv }} + continueOnError: ${{ parameters.continueOnError }} + condition: ${{ parameters.condition }} +- ${{ else }}: + - task: MicroBuildSigningPlugin@4 + displayName: Install MicroBuild plugin + inputs: ${{ parameters.microbuildTaskInputs }} + env: ${{ parameters.microbuildEnv }} + continueOnError: ${{ parameters.continueOnError }} + condition: ${{ parameters.condition }} diff --git a/eng/common/core-templates/steps/install-microbuild.yml b/eng/common/core-templates/steps/install-microbuild.yml index 553fce66b94..76a54e157fd 100644 --- a/eng/common/core-templates/steps/install-microbuild.yml +++ b/eng/common/core-templates/steps/install-microbuild.yml @@ -4,6 +4,8 @@ parameters: # Enable install tasks for MicroBuild on Mac and Linux # Will be ignored if 'enableMicrobuild' is false or 'Agent.Os' is 'Windows_NT' enableMicrobuildForMacAndLinux: false + # Enable preview version of MB signing plugin + enablePreviewMicrobuild: false # Determines whether the ESRP service connection information should be passed to the signing plugin. # This overlaps with _SignType to some degree. We only need the service connection for real signing. # It's important that the service connection not be passed to the MicroBuildSigningPlugin task in this place. @@ -13,6 +15,8 @@ parameters: microbuildUseESRP: true # Microbuild installation directory microBuildOutputFolder: $(Agent.TempDirectory)/MicroBuild + # Microbuild version + microbuildPluginVersion: 'latest' continueOnError: false @@ -69,42 +73,46 @@ steps: # YAML expansion, and Windows vs. Linux/Mac uses different service connections. However, # we can avoid including the MB install step if not enabled at all. This avoids a bunch of # extra pipeline authorizations, since most pipelines do not sign on non-Windows. - - task: MicroBuildSigningPlugin@4 - displayName: Install MicroBuild plugin (Windows) - inputs: - signType: $(_SignType) - zipSources: false - feedSource: https://dnceng.pkgs.visualstudio.com/_packaging/MicroBuildToolset/nuget/v3/index.json - ${{ if eq(parameters.microbuildUseESRP, true) }}: - ConnectedServiceName: 'MicroBuild Signing Task (DevDiv)' - ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - ConnectedPMEServiceName: 6cc74545-d7b9-4050-9dfa-ebefcc8961ea - ${{ else }}: - ConnectedPMEServiceName: 248d384a-b39b-46e3-8ad5-c2c210d5e7ca - env: - TeamName: $(_TeamName) - MicroBuildOutputFolderOverride: ${{ parameters.microBuildOutputFolder }} - SYSTEM_ACCESSTOKEN: $(System.AccessToken) - continueOnError: ${{ parameters.continueOnError }} - condition: and(succeeded(), eq(variables['Agent.Os'], 'Windows_NT'), in(variables['_SignType'], 'real', 'test')) - - - ${{ if eq(parameters.enableMicrobuildForMacAndLinux, true) }}: - - task: MicroBuildSigningPlugin@4 - displayName: Install MicroBuild plugin (non-Windows) - inputs: + - template: /eng/common/core-templates/steps/install-microbuild-impl.yml + parameters: + enablePreviewMicrobuild: ${{ parameters.enablePreviewMicrobuild }} + microbuildTaskInputs: signType: $(_SignType) zipSources: false feedSource: https://dnceng.pkgs.visualstudio.com/_packaging/MicroBuildToolset/nuget/v3/index.json - workingDirectory: ${{ parameters.microBuildOutputFolder }} + version: ${{ parameters.microbuildPluginVersion }} ${{ if eq(parameters.microbuildUseESRP, true) }}: ConnectedServiceName: 'MicroBuild Signing Task (DevDiv)' ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: - ConnectedPMEServiceName: beb8cb23-b303-4c95-ab26-9e44bc958d39 + ConnectedPMEServiceName: 6cc74545-d7b9-4050-9dfa-ebefcc8961ea ${{ else }}: - ConnectedPMEServiceName: c24de2a5-cc7a-493d-95e4-8e5ff5cad2bc - env: + ConnectedPMEServiceName: 248d384a-b39b-46e3-8ad5-c2c210d5e7ca + microbuildEnv: TeamName: $(_TeamName) MicroBuildOutputFolderOverride: ${{ parameters.microBuildOutputFolder }} SYSTEM_ACCESSTOKEN: $(System.AccessToken) continueOnError: ${{ parameters.continueOnError }} - condition: and(succeeded(), ne(variables['Agent.Os'], 'Windows_NT'), eq(variables['_SignType'], 'real')) + condition: and(succeeded(), eq(variables['Agent.Os'], 'Windows_NT'), in(variables['_SignType'], 'real', 'test')) + + - ${{ if eq(parameters.enableMicrobuildForMacAndLinux, true) }}: + - template: /eng/common/core-templates/steps/install-microbuild-impl.yml + parameters: + enablePreviewMicrobuild: ${{ parameters.enablePreviewMicrobuild }} + microbuildTaskInputs: + signType: $(_SignType) + zipSources: false + feedSource: https://dnceng.pkgs.visualstudio.com/_packaging/MicroBuildToolset/nuget/v3/index.json + version: ${{ parameters.microbuildPluginVersion }} + workingDirectory: ${{ parameters.microBuildOutputFolder }} + ${{ if eq(parameters.microbuildUseESRP, true) }}: + ConnectedServiceName: 'MicroBuild Signing Task (DevDiv)' + ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}: + ConnectedPMEServiceName: beb8cb23-b303-4c95-ab26-9e44bc958d39 + ${{ else }}: + ConnectedPMEServiceName: c24de2a5-cc7a-493d-95e4-8e5ff5cad2bc + microbuildEnv: + TeamName: $(_TeamName) + MicroBuildOutputFolderOverride: ${{ parameters.microBuildOutputFolder }} + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + continueOnError: ${{ parameters.continueOnError }} + condition: and(succeeded(), ne(variables['Agent.Os'], 'Windows_NT'), eq(variables['_SignType'], 'real')) diff --git a/eng/common/core-templates/steps/publish-logs.yml b/eng/common/core-templates/steps/publish-logs.yml index 4eed0312b80..84a1922c73f 100644 --- a/eng/common/core-templates/steps/publish-logs.yml +++ b/eng/common/core-templates/steps/publish-logs.yml @@ -61,3 +61,4 @@ steps: condition: always() retryCountOnTaskFailure: 10 # for any files being locked isProduction: false # logs are non-production artifacts + diff --git a/eng/common/core-templates/steps/source-build.yml b/eng/common/core-templates/steps/source-build.yml index 09ae5cd73ae..b75f59c428d 100644 --- a/eng/common/core-templates/steps/source-build.yml +++ b/eng/common/core-templates/steps/source-build.yml @@ -24,7 +24,7 @@ steps: # in the default public locations. internalRuntimeDownloadArgs= if [ '$(dotnetbuilds-internal-container-read-token-base64)' != '$''(dotnetbuilds-internal-container-read-token-base64)' ]; then - internalRuntimeDownloadArgs='/p:DotNetRuntimeSourceFeed=https://ci.dot.net/internal /p:DotNetRuntimeSourceFeedKey=$(dotnetbuilds-internal-container-read-token-base64) --runtimesourcefeed https://ci.dot.net/internal --runtimesourcefeedkey '$(dotnetbuilds-internal-container-read-token-base64)'' + internalRuntimeDownloadArgs='/p:DotNetRuntimeSourceFeed=https://ci.dot.net/internal /p:DotNetRuntimeSourceFeedKey=$(dotnetbuilds-internal-container-read-token-base64) --runtimesourcefeed https://ci.dot.net/internal --runtimesourcefeedkey $(dotnetbuilds-internal-container-read-token-base64)' fi buildConfig=Release diff --git a/eng/common/core-templates/steps/source-index-stage1-publish.yml b/eng/common/core-templates/steps/source-index-stage1-publish.yml index e9a694afa58..3ad83b8c307 100644 --- a/eng/common/core-templates/steps/source-index-stage1-publish.yml +++ b/eng/common/core-templates/steps/source-index-stage1-publish.yml @@ -1,6 +1,6 @@ parameters: - sourceIndexUploadPackageVersion: 2.0.0-20250818.1 - sourceIndexProcessBinlogPackageVersion: 1.0.1-20250818.1 + sourceIndexUploadPackageVersion: 2.0.0-20250906.1 + sourceIndexProcessBinlogPackageVersion: 1.0.1-20250906.1 sourceIndexPackageSource: https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json binlogPath: artifacts/log/Debug/Build.binlog @@ -14,8 +14,8 @@ steps: workingDirectory: $(Agent.TempDirectory) - script: | - $(Agent.TempDirectory)/dotnet/dotnet tool install BinLogToSln --version ${{parameters.sourceIndexProcessBinlogPackageVersion}} --add-source ${{parameters.SourceIndexPackageSource}} --tool-path $(Agent.TempDirectory)/.source-index/tools - $(Agent.TempDirectory)/dotnet/dotnet tool install UploadIndexStage1 --version ${{parameters.sourceIndexUploadPackageVersion}} --add-source ${{parameters.SourceIndexPackageSource}} --tool-path $(Agent.TempDirectory)/.source-index/tools + $(Agent.TempDirectory)/dotnet/dotnet tool install BinLogToSln --version ${{parameters.sourceIndexProcessBinlogPackageVersion}} --source ${{parameters.sourceIndexPackageSource}} --tool-path $(Agent.TempDirectory)/.source-index/tools + $(Agent.TempDirectory)/dotnet/dotnet tool install UploadIndexStage1 --version ${{parameters.sourceIndexUploadPackageVersion}} --source ${{parameters.sourceIndexPackageSource}} --tool-path $(Agent.TempDirectory)/.source-index/tools displayName: "Source Index: Download netsourceindex Tools" # Set working directory to temp directory so 'dotnet' doesn't try to use global.json and use the repo's sdk. workingDirectory: $(Agent.TempDirectory) diff --git a/eng/common/cross/build-rootfs.sh b/eng/common/cross/build-rootfs.sh index 8abfb71f727..314c93c5759 100755 --- a/eng/common/cross/build-rootfs.sh +++ b/eng/common/cross/build-rootfs.sh @@ -9,6 +9,7 @@ usage() echo "CodeName - optional, Code name for Linux, can be: xenial(default), zesty, bionic, alpine" echo " for alpine can be specified with version: alpineX.YY or alpineedge" echo " for FreeBSD can be: freebsd13, freebsd14" + echo " for OpenBSD can be: openbsd" echo " for illumos can be: illumos" echo " for Haiku can be: haiku." echo "lldbx.y - optional, LLDB version, can be: lldb3.9(default), lldb4.0, lldb5.0, lldb6.0 no-lldb. Ignored for alpine and FreeBSD" @@ -27,6 +28,8 @@ __BuildArch=arm __AlpineArch=armv7 __FreeBSDArch=arm __FreeBSDMachineArch=armv7 +__OpenBSDArch=arm +__OpenBSDMachineArch=armv7 __IllumosArch=arm7 __HaikuArch=arm __QEMUArch=arm @@ -72,7 +75,7 @@ __AlpinePackages+=" krb5-dev" __AlpinePackages+=" openssl-dev" __AlpinePackages+=" zlib-dev" -__FreeBSDBase="13.4-RELEASE" +__FreeBSDBase="13.5-RELEASE" __FreeBSDPkg="1.21.3" __FreeBSDABI="13" __FreeBSDPackages="libunwind" @@ -82,6 +85,12 @@ __FreeBSDPackages+=" openssl" __FreeBSDPackages+=" krb5" __FreeBSDPackages+=" terminfo-db" +__OpenBSDVersion="7.8" +__OpenBSDPackages="heimdal-libs" +__OpenBSDPackages+=" icu4c" +__OpenBSDPackages+=" inotify-tools" +__OpenBSDPackages+=" openssl" + __IllumosPackages="icu" __IllumosPackages+=" mit-krb5" __IllumosPackages+=" openssl" @@ -160,6 +169,8 @@ while :; do __QEMUArch=aarch64 __FreeBSDArch=arm64 __FreeBSDMachineArch=aarch64 + __OpenBSDArch=arm64 + __OpenBSDMachineArch=aarch64 ;; armel) __BuildArch=armel @@ -235,6 +246,8 @@ while :; do __UbuntuArch=amd64 __FreeBSDArch=amd64 __FreeBSDMachineArch=amd64 + __OpenBSDArch=amd64 + __OpenBSDMachineArch=amd64 __illumosArch=x86_64 __HaikuArch=x86_64 __UbuntuRepo="http://archive.ubuntu.com/ubuntu/" @@ -295,9 +308,7 @@ while :; do ;; noble) # Ubuntu 24.04 __CodeName=noble - if [[ -z "$__LLDB_Package" ]]; then - __LLDB_Package="liblldb-19-dev" - fi + __LLDB_Package="liblldb-19-dev" ;; stretch) # Debian 9 __CodeName=stretch @@ -383,10 +394,14 @@ while :; do ;; freebsd14) __CodeName=freebsd - __FreeBSDBase="14.2-RELEASE" + __FreeBSDBase="14.3-RELEASE" __FreeBSDABI="14" __SkipUnmount=1 ;; + openbsd) + __CodeName=openbsd + __SkipUnmount=1 + ;; illumos) __CodeName=illumos __SkipUnmount=1 @@ -595,6 +610,62 @@ elif [[ "$__CodeName" == "freebsd" ]]; then INSTALL_AS_USER=$(whoami) "$__RootfsDir"/host/sbin/pkg -r "$__RootfsDir" -C "$__RootfsDir"/usr/local/etc/pkg.conf update # shellcheck disable=SC2086 INSTALL_AS_USER=$(whoami) "$__RootfsDir"/host/sbin/pkg -r "$__RootfsDir" -C "$__RootfsDir"/usr/local/etc/pkg.conf install --yes $__FreeBSDPackages +elif [[ "$__CodeName" == "openbsd" ]]; then + # determine mirrors + OPENBSD_MIRROR="https://cdn.openbsd.org/pub/OpenBSD/$__OpenBSDVersion/$__OpenBSDMachineArch" + + # download base system sets + ensureDownloadTool + + BASE_SETS=(base comp) + for set in "${BASE_SETS[@]}"; do + FILE="${set}${__OpenBSDVersion//./}.tgz" + echo "Downloading $FILE..." + if [[ "$__hasWget" == 1 ]]; then + wget -O- "$OPENBSD_MIRROR/$FILE" | tar -C "$__RootfsDir" -xzpf - + else + curl -SL "$OPENBSD_MIRROR/$FILE" | tar -C "$__RootfsDir" -xzpf - + fi + done + + PKG_MIRROR="https://cdn.openbsd.org/pub/OpenBSD/${__OpenBSDVersion}/packages/${__OpenBSDMachineArch}" + + echo "Installing packages into sysroot..." + + # Fetch package index once + if [[ "$__hasWget" == 1 ]]; then + PKG_INDEX=$(wget -qO- "$PKG_MIRROR/") + else + PKG_INDEX=$(curl -s "$PKG_MIRROR/") + fi + + for pkg in $__OpenBSDPackages; do + PKG_FILE=$(echo "$PKG_INDEX" | grep -Po ">\K${pkg}-[0-9][^\" ]*\.tgz" \ + | sort -V | tail -n1) + + echo "Resolved package filename for $pkg: $PKG_FILE" + + [[ -z "$PKG_FILE" ]] && { echo "ERROR: Package $pkg not found"; exit 1; } + + if [[ "$__hasWget" == 1 ]]; then + wget -O- "$PKG_MIRROR/$PKG_FILE" | tar -C "$__RootfsDir" -xzpf - + else + curl -SL "$PKG_MIRROR/$PKG_FILE" | tar -C "$__RootfsDir" -xzpf - + fi + done + + echo "Creating versionless symlinks for shared libraries..." + # Find all versioned .so files and create the base .so symlink + for lib in "$__RootfsDir/usr/lib/libc++.so."* "$__RootfsDir/usr/lib/libc++abi.so."* "$__RootfsDir/usr/lib/libpthread.so."*; do + if [ -f "$lib" ]; then + # Extract the filename (e.g., libc++.so.12.0) + VERSIONED_NAME=$(basename "$lib") + # Remove the trailing version numbers (e.g., libc++.so) + BASE_NAME=${VERSIONED_NAME%.so.*}.so + # Create the symlink in the same directory + ln -sf "$VERSIONED_NAME" "$__RootfsDir/usr/lib/$BASE_NAME" + fi + done elif [[ "$__CodeName" == "illumos" ]]; then mkdir "$__RootfsDir/tmp" pushd "$__RootfsDir/tmp" diff --git a/eng/common/cross/toolchain.cmake b/eng/common/cross/toolchain.cmake index 0ff85cf0367..ff2dfdb4a5b 100644 --- a/eng/common/cross/toolchain.cmake +++ b/eng/common/cross/toolchain.cmake @@ -3,15 +3,22 @@ set(CROSS_ROOTFS $ENV{ROOTFS_DIR}) # reset platform variables (e.g. cmake 3.25 sets LINUX=1) unset(LINUX) unset(FREEBSD) +unset(OPENBSD) unset(ILLUMOS) unset(ANDROID) unset(TIZEN) unset(HAIKU) set(TARGET_ARCH_NAME $ENV{TARGET_BUILD_ARCH}) + +file(GLOB OPENBSD_PROBE "${CROSS_ROOTFS}/etc/signify/openbsd-*.pub") + if(EXISTS ${CROSS_ROOTFS}/bin/freebsd-version) set(CMAKE_SYSTEM_NAME FreeBSD) set(FREEBSD 1) +elseif(OPENBSD_PROBE) + set(CMAKE_SYSTEM_NAME OpenBSD) + set(OPENBSD 1) elseif(EXISTS ${CROSS_ROOTFS}/usr/platform/i86pc) set(CMAKE_SYSTEM_NAME SunOS) set(ILLUMOS 1) @@ -53,6 +60,8 @@ elseif(TARGET_ARCH_NAME STREQUAL "arm64") endif() elseif(FREEBSD) set(triple "aarch64-unknown-freebsd12") + elseif(OPENBSD) + set(triple "aarch64-unknown-openbsd") endif() elseif(TARGET_ARCH_NAME STREQUAL "armel") set(CMAKE_SYSTEM_PROCESSOR armv7l) @@ -109,6 +118,8 @@ elseif(TARGET_ARCH_NAME STREQUAL "x64") endif() elseif(FREEBSD) set(triple "x86_64-unknown-freebsd12") + elseif(OPENBSD) + set(triple "x86_64-unknown-openbsd") elseif(ILLUMOS) set(TOOLCHAIN "x86_64-illumos") elseif(HAIKU) @@ -193,7 +204,7 @@ if(ANDROID) # include official NDK toolchain script include(${CROSS_ROOTFS}/../build/cmake/android.toolchain.cmake) -elseif(FREEBSD) +elseif(FREEBSD OR OPENBSD) # we cross-compile by instructing clang set(CMAKE_C_COMPILER_TARGET ${triple}) set(CMAKE_CXX_COMPILER_TARGET ${triple}) @@ -291,7 +302,7 @@ endif() # Specify compile options -if((TARGET_ARCH_NAME MATCHES "^(arm|arm64|armel|armv6|loongarch64|ppc64le|riscv64|s390x|x64|x86)$" AND NOT ANDROID AND NOT FREEBSD) OR ILLUMOS OR HAIKU) +if((TARGET_ARCH_NAME MATCHES "^(arm|arm64|armel|armv6|loongarch64|ppc64le|riscv64|s390x|x64|x86)$" AND NOT ANDROID AND NOT FREEBSD AND NOT OPENBSD) OR ILLUMOS OR HAIKU) set(CMAKE_C_COMPILER_TARGET ${TOOLCHAIN}) set(CMAKE_CXX_COMPILER_TARGET ${TOOLCHAIN}) set(CMAKE_ASM_COMPILER_TARGET ${TOOLCHAIN}) diff --git a/eng/common/darc-init.ps1 b/eng/common/darc-init.ps1 index e3374310563..a5be41db690 100644 --- a/eng/common/darc-init.ps1 +++ b/eng/common/darc-init.ps1 @@ -29,11 +29,11 @@ function InstallDarcCli ($darcVersion, $toolpath) { Write-Host "Installing Darc CLI version $darcVersion..." Write-Host 'You may need to restart your command window if this is the first dotnet tool you have installed.' if (-not $toolpath) { - Write-Host "'$dotnet' tool install $darcCliPackageName --version $darcVersion --add-source '$arcadeServicesSource' -v $verbosity -g" - & "$dotnet" tool install $darcCliPackageName --version $darcVersion --add-source "$arcadeServicesSource" -v $verbosity -g + Write-Host "'$dotnet' tool install $darcCliPackageName --version $darcVersion --source '$arcadeServicesSource' -v $verbosity -g" + & "$dotnet" tool install $darcCliPackageName --version $darcVersion --source "$arcadeServicesSource" -v $verbosity -g }else { - Write-Host "'$dotnet' tool install $darcCliPackageName --version $darcVersion --add-source '$arcadeServicesSource' -v $verbosity --tool-path '$toolpath'" - & "$dotnet" tool install $darcCliPackageName --version $darcVersion --add-source "$arcadeServicesSource" -v $verbosity --tool-path "$toolpath" + Write-Host "'$dotnet' tool install $darcCliPackageName --version $darcVersion --source '$arcadeServicesSource' -v $verbosity --tool-path '$toolpath'" + & "$dotnet" tool install $darcCliPackageName --version $darcVersion --source "$arcadeServicesSource" -v $verbosity --tool-path "$toolpath" } } diff --git a/eng/common/darc-init.sh b/eng/common/darc-init.sh index e889f439b8d..b56d40e5706 100755 --- a/eng/common/darc-init.sh +++ b/eng/common/darc-init.sh @@ -5,7 +5,7 @@ darcVersion='' versionEndpoint='https://maestro.dot.net/api/assets/darc-version?api-version=2020-02-20' verbosity='minimal' -while [[ $# > 0 ]]; do +while [[ $# -gt 0 ]]; do opt="$(echo "$1" | tr "[:upper:]" "[:lower:]")" case "$opt" in --darcversion) @@ -73,9 +73,9 @@ function InstallDarcCli { echo "Installing Darc CLI version $darcVersion..." echo "You may need to restart your command shell if this is the first dotnet tool you have installed." if [ -z "$toolpath" ]; then - echo $($dotnet_root/dotnet tool install $darc_cli_package_name --version $darcVersion --add-source "$arcadeServicesSource" -v $verbosity -g) + echo $($dotnet_root/dotnet tool install $darc_cli_package_name --version $darcVersion --source "$arcadeServicesSource" -v $verbosity -g) else - echo $($dotnet_root/dotnet tool install $darc_cli_package_name --version $darcVersion --add-source "$arcadeServicesSource" -v $verbosity --tool-path "$toolpath") + echo $($dotnet_root/dotnet tool install $darc_cli_package_name --version $darcVersion --source "$arcadeServicesSource" -v $verbosity --tool-path "$toolpath") fi } diff --git a/eng/common/dotnet-install.sh b/eng/common/dotnet-install.sh index 7b9d97e3bd4..61f302bb677 100755 --- a/eng/common/dotnet-install.sh +++ b/eng/common/dotnet-install.sh @@ -18,7 +18,7 @@ architecture='' runtime='dotnet' runtimeSourceFeed='' runtimeSourceFeedKey='' -while [[ $# > 0 ]]; do +while [[ $# -gt 0 ]]; do opt="$(echo "$1" | tr "[:upper:]" "[:lower:]")" case "$opt" in -version|-v) diff --git a/eng/common/dotnet.sh b/eng/common/dotnet.sh index 2ef68235675..f6d24871c1d 100755 --- a/eng/common/dotnet.sh +++ b/eng/common/dotnet.sh @@ -19,7 +19,7 @@ source $scriptroot/tools.sh InitializeDotNetCli true # install # Invoke acquired SDK with args if they are provided -if [[ $# > 0 ]]; then +if [[ $# -gt 0 ]]; then __dotnetDir=${_InitializeDotNetCli} dotnetPath=${__dotnetDir}/dotnet ${dotnetPath} "$@" diff --git a/eng/common/internal-feed-operations.sh b/eng/common/internal-feed-operations.sh index 9378223ba09..6299e7effd4 100755 --- a/eng/common/internal-feed-operations.sh +++ b/eng/common/internal-feed-operations.sh @@ -100,7 +100,7 @@ operation='' authToken='' repoName='' -while [[ $# > 0 ]]; do +while [[ $# -gt 0 ]]; do opt="$(echo "$1" | tr "[:upper:]" "[:lower:]")" case "$opt" in --operation) diff --git a/eng/common/native/init-distro-rid.sh b/eng/common/native/init-distro-rid.sh index 83ea7aab0e0..8fc6d2fec78 100644 --- a/eng/common/native/init-distro-rid.sh +++ b/eng/common/native/init-distro-rid.sh @@ -39,6 +39,8 @@ getNonPortableDistroRid() # $rootfsDir can be empty. freebsd-version is a shell script and should always work. __freebsd_major_version=$("$rootfsDir"/bin/freebsd-version | cut -d'.' -f1) nonPortableRid="freebsd.$__freebsd_major_version-${targetArch}" + elif [ "$targetOs" = "openbsd" ]; then + nonPortableRid="openbsd.$(uname -r)-${targetArch}" elif command -v getprop >/dev/null && getprop ro.product.system.model | grep -qi android; then __android_sdk_version=$(getprop ro.build.version.sdk) nonPortableRid="android.$__android_sdk_version-${targetArch}" diff --git a/eng/common/native/install-dependencies.sh b/eng/common/native/install-dependencies.sh index 477a44f335b..4742177a768 100644 --- a/eng/common/native/install-dependencies.sh +++ b/eng/common/native/install-dependencies.sh @@ -24,14 +24,16 @@ case "$os" in apt update apt install -y build-essential gettext locales cmake llvm clang lld lldb liblldb-dev libunwind8-dev libicu-dev liblttng-ust-dev \ - libssl-dev libkrb5-dev pigz cpio + libssl-dev libkrb5-dev pigz cpio ninja-build localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 - elif [ "$ID" = "fedora" ] || [ "$ID" = "rhel" ] || [ "$ID" = "azurelinux" ]; then + elif [ "$ID" = "fedora" ] || [ "$ID" = "rhel" ] || [ "$ID" = "azurelinux" ] || [ "$ID" = "centos" ]; then pkg_mgr="$(command -v tdnf 2>/dev/null || command -v dnf)" - $pkg_mgr install -y cmake llvm lld lldb clang python curl libicu-devel openssl-devel krb5-devel lttng-ust-devel pigz cpio + $pkg_mgr install -y cmake llvm lld lldb clang python curl libicu-devel openssl-devel krb5-devel lttng-ust-devel pigz cpio ninja-build + elif [ "$ID" = "amzn" ]; then + dnf install -y cmake llvm lld lldb clang python libicu-devel openssl-devel krb5-devel lttng-ust-devel pigz cpio ninja-build elif [ "$ID" = "alpine" ]; then - apk add build-base cmake bash curl clang llvm-dev lld lldb krb5-dev lttng-ust-dev icu-dev openssl-dev pigz cpio + apk add build-base cmake bash curl clang llvm llvm-dev lld lldb-dev krb5-dev lttng-ust-dev icu-dev openssl-dev pigz cpio ninja else echo "Unsupported distro. distro: $ID" exit 1 @@ -52,6 +54,7 @@ brew "openssl@3" brew "pkgconf" brew "python3" brew "pigz" +brew "ninja" EOF ;; diff --git a/eng/common/post-build/redact-logs.ps1 b/eng/common/post-build/redact-logs.ps1 index 472d5bb562c..672f4e2652e 100644 --- a/eng/common/post-build/redact-logs.ps1 +++ b/eng/common/post-build/redact-logs.ps1 @@ -9,7 +9,8 @@ param( [Parameter(Mandatory=$false)][string] $TokensFilePath, [Parameter(ValueFromRemainingArguments=$true)][String[]]$TokensToRedact, [Parameter(Mandatory=$false)][string] $runtimeSourceFeed, - [Parameter(Mandatory=$false)][string] $runtimeSourceFeedKey) + [Parameter(Mandatory=$false)][string] $runtimeSourceFeedKey +) try { $ErrorActionPreference = 'Stop' @@ -48,8 +49,8 @@ try { Write-Host "Installing Binlog redactor CLI..." Write-Host "'$dotnet' new tool-manifest" & "$dotnet" new tool-manifest - Write-Host "'$dotnet' tool install $packageName --local --add-source '$PackageFeed' -v $verbosity --version $BinlogToolVersion" - & "$dotnet" tool install $packageName --local --add-source "$PackageFeed" -v $verbosity --version $BinlogToolVersion + Write-Host "'$dotnet' tool install $packageName --local --source '$PackageFeed' -v $verbosity --version $BinlogToolVersion" + & "$dotnet" tool install $packageName --local --source "$PackageFeed" -v $verbosity --version $BinlogToolVersion if (Test-Path $TokensFilePath) { Write-Host "Adding additional sensitive data for redaction from file: " $TokensFilePath diff --git a/eng/common/renovate.env b/eng/common/renovate.env new file mode 100644 index 00000000000..17ecc05d9b1 --- /dev/null +++ b/eng/common/renovate.env @@ -0,0 +1,42 @@ +# Renovate Global Configuration +# https://docs.renovatebot.com/self-hosted-configuration/ +# +# NOTE: This file uses bash/shell format and is sourced via `. renovate.env`. +# Values containing spaces or special characters must be quoted. + +# Author to use for git commits made by Renovate +# https://docs.renovatebot.com/configuration-options/#gitauthor +export RENOVATE_GIT_AUTHOR='.NET Renovate ' + +# Disable rate limiting for PR creation (0 = unlimited) +# https://docs.renovatebot.com/presets-default/#prhourlylimitnone +# https://docs.renovatebot.com/presets-default/#prconcurrentlimitnone +export RENOVATE_PR_HOURLY_LIMIT=0 +export RENOVATE_PR_CONCURRENT_LIMIT=0 + +# Skip the onboarding PR that Renovate normally creates for new repos +# https://docs.renovatebot.com/config-overview/#onboarding +export RENOVATE_ONBOARDING=false + +# Any Renovate config file in the cloned repository is ignored. Only +# the Renovate config file from the repo where the pipeline is running +# is used (yes, those are the same repo but the sources may be different). +# https://docs.renovatebot.com/self-hosted-configuration/#requireconfig +export RENOVATE_REQUIRE_CONFIG=ignored + +# Customize the PR body content. This removes some of the default +# sections that aren't relevant in a self-hosted config. +# https://docs.renovatebot.com/configuration-options/#prheader +# https://docs.renovatebot.com/configuration-options/#prbodynotes +# https://docs.renovatebot.com/configuration-options/#prbodytemplate +export RENOVATE_PR_HEADER='## Automated Dependency Update' +export RENOVATE_PR_BODY_NOTES='["This PR has been created automatically by the [.NET Renovate Bot](https://github.com/dotnet/arcade/blob/main/Documentation/Renovate.md) to update one or more dependencies in your repo. Please review the changes and merge the PR if everything looks good."]' +export RENOVATE_PR_BODY_TEMPLATE='{{{header}}}{{{table}}}{{{warnings}}}{{{notes}}}{{{changelogs}}}' + +# Extend the global config with additional presets +# https://docs.renovatebot.com/self-hosted-configuration/#globalextends +# Disable the Dependency Dashboard issue that tracks all updates +export RENOVATE_GLOBAL_EXTENDS='[":disableDependencyDashboard"]' + +# Allow all commands for post-upgrade commands. +export RENOVATE_ALLOWED_COMMANDS='[".*"]' diff --git a/eng/common/sdk-task.ps1 b/eng/common/sdk-task.ps1 index b64b66a6275..64fd2f8abec 100644 --- a/eng/common/sdk-task.ps1 +++ b/eng/common/sdk-task.ps1 @@ -66,20 +66,7 @@ try { if( $msbuildEngine -eq "vs") { # Ensure desktop MSBuild is available for sdk tasks. - if( -not ($GlobalJson.tools.PSObject.Properties.Name -contains "vs" )) { - $GlobalJson.tools | Add-Member -Name "vs" -Value (ConvertFrom-Json "{ `"version`": `"16.5`" }") -MemberType NoteProperty - } - if( -not ($GlobalJson.tools.PSObject.Properties.Name -match "xcopy-msbuild" )) { - $GlobalJson.tools | Add-Member -Name "xcopy-msbuild" -Value "18.0.0" -MemberType NoteProperty - } - if ($GlobalJson.tools."xcopy-msbuild".Trim() -ine "none") { - $xcopyMSBuildToolsFolder = InitializeXCopyMSBuild $GlobalJson.tools."xcopy-msbuild" -install $true - } - if ($xcopyMSBuildToolsFolder -eq $null) { - throw 'Unable to get xcopy downloadable version of msbuild' - } - - $global:_MSBuildExe = "$($xcopyMSBuildToolsFolder)\MSBuild\Current\Bin\MSBuild.exe" + $global:_MSBuildExe = InitializeVisualStudioMSBuild } $taskProject = GetSdkTaskProject $task diff --git a/eng/common/template-guidance.md b/eng/common/template-guidance.md index e2b07a865f1..f772aa3d78f 100644 --- a/eng/common/template-guidance.md +++ b/eng/common/template-guidance.md @@ -71,7 +71,6 @@ eng\common\ source-build.yml (shim) source-index-stage1.yml (shim) jobs\ - codeql-build.yml (shim) jobs.yml (shim) source-build.yml (shim) post-build\ @@ -88,7 +87,6 @@ eng\common\ source-build.yml (shim) variables\ pool-providers.yml (logic + redirect) # templates/variables/pool-providers.yml will redirect to templates-official/variables/pool-providers.yml if you are running in the internal project - sdl-variables.yml (logic) core-templates\ job\ job.yml (logic) @@ -97,7 +95,6 @@ eng\common\ source-build.yml (logic) source-index-stage1.yml (logic) jobs\ - codeql-build.yml (logic) jobs.yml (logic) source-build.yml (logic) post-build\ diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index 977a2d4b103..e28db6c7c8f 100644 --- a/eng/common/tools.ps1 +++ b/eng/common/tools.ps1 @@ -34,6 +34,9 @@ # Configures warning treatment in msbuild. [bool]$warnAsError = if (Test-Path variable:warnAsError) { $warnAsError } else { $true } +# Specifies semi-colon delimited list of warning codes that should not be treated as errors. +[string]$warnNotAsError = if (Test-Path variable:warnNotAsError) { $warnNotAsError } else { '' } + # Specifies which msbuild engine to use for build: 'vs', 'dotnet' or unspecified (determined based on presence of tools.vs in global.json). [string]$msbuildEngine = if (Test-Path variable:msbuildEngine) { $msbuildEngine } else { $null } @@ -157,9 +160,6 @@ function InitializeDotNetCli([bool]$install, [bool]$createSdkLocationFile) { return $global:_DotNetInstallDir } - # Don't resolve runtime, shared framework, or SDK from other locations to ensure build determinism - $env:DOTNET_MULTILEVEL_LOOKUP=0 - # Disable first run since we do not need all ASP.NET packages restored. $env:DOTNET_NOLOGO=1 @@ -185,7 +185,11 @@ function InitializeDotNetCli([bool]$install, [bool]$createSdkLocationFile) { if ((-not $globalJsonHasRuntimes) -and (-not [string]::IsNullOrEmpty($env:DOTNET_INSTALL_DIR)) -and (Test-Path(Join-Path $env:DOTNET_INSTALL_DIR "sdk\$dotnetSdkVersion"))) { $dotnetRoot = $env:DOTNET_INSTALL_DIR } else { - $dotnetRoot = Join-Path $RepoRoot '.dotnet' + if (-not [string]::IsNullOrEmpty($env:DOTNET_GLOBAL_INSTALL_DIR)) { + $dotnetRoot = $env:DOTNET_GLOBAL_INSTALL_DIR + } else { + $dotnetRoot = Join-Path $RepoRoot '.dotnet' + } if (-not (Test-Path(Join-Path $dotnetRoot "sdk\$dotnetSdkVersion"))) { if ($install) { @@ -225,7 +229,6 @@ function InitializeDotNetCli([bool]$install, [bool]$createSdkLocationFile) { # Make Sure that our bootstrapped dotnet cli is available in future steps of the Azure Pipelines build Write-PipelinePrependPath -Path $dotnetRoot - Write-PipelineSetVariable -Name 'DOTNET_MULTILEVEL_LOOKUP' -Value '0' Write-PipelineSetVariable -Name 'DOTNET_NOLOGO' -Value '1' return $global:_DotNetInstallDir = $dotnetRoot @@ -299,6 +302,8 @@ function InstallDotNet([string] $dotnetRoot, $dotnetVersionLabel = "'sdk v$version'" + # For performance this check is duplicated in src/Microsoft.DotNet.Arcade.Sdk/src/InstallDotNetCore.cs + # if you are making changes here, consider if you need to make changes there as well. if ($runtime -ne '' -and $runtime -ne 'sdk') { $runtimePath = $dotnetRoot $runtimePath = $runtimePath + "\shared" @@ -374,12 +379,11 @@ function InstallDotNet([string] $dotnetRoot, # # 1. MSBuild from an active VS command prompt # 2. MSBuild from a compatible VS installation -# 3. MSBuild from the xcopy tool package # # Returns full path to msbuild.exe. # Throws on failure. # -function InitializeVisualStudioMSBuild([bool]$install, [object]$vsRequirements = $null) { +function InitializeVisualStudioMSBuild([object]$vsRequirements = $null) { if (-not (IsWindowsPlatform)) { throw "Cannot initialize Visual Studio on non-Windows" } @@ -389,13 +393,7 @@ function InitializeVisualStudioMSBuild([bool]$install, [object]$vsRequirements = } # Minimum VS version to require. - $vsMinVersionReqdStr = '17.7' - $vsMinVersionReqd = [Version]::new($vsMinVersionReqdStr) - - # If the version of msbuild is going to be xcopied, - # use this version. Version matches a package here: - # https://dev.azure.com/dnceng/public/_artifacts/feed/dotnet-eng/NuGet/Microsoft.DotNet.Arcade.MSBuild.Xcopy/versions/18.0.0 - $defaultXCopyMSBuildVersion = '18.0.0' + $vsMinVersionReqdStr = '18.0' if (!$vsRequirements) { if (Get-Member -InputObject $GlobalJson.tools -Name 'vs') { @@ -425,46 +423,16 @@ function InitializeVisualStudioMSBuild([bool]$install, [object]$vsRequirements = } } - # Locate Visual Studio installation or download x-copy msbuild. + # Locate Visual Studio installation. $vsInfo = LocateVisualStudio $vsRequirements - if ($vsInfo -ne $null -and $env:ForceUseXCopyMSBuild -eq $null) { + if ($vsInfo -ne $null) { # Ensure vsInstallDir has a trailing slash $vsInstallDir = Join-Path $vsInfo.installationPath "\" $vsMajorVersion = $vsInfo.installationVersion.Split('.')[0] InitializeVisualStudioEnvironmentVariables $vsInstallDir $vsMajorVersion } else { - if (Get-Member -InputObject $GlobalJson.tools -Name 'xcopy-msbuild') { - $xcopyMSBuildVersion = $GlobalJson.tools.'xcopy-msbuild' - $vsMajorVersion = $xcopyMSBuildVersion.Split('.')[0] - } else { - #if vs version provided in global.json is incompatible (too low) then use the default version for xcopy msbuild download - if($vsMinVersion -lt $vsMinVersionReqd){ - Write-Host "Using xcopy-msbuild version of $defaultXCopyMSBuildVersion since VS version $vsMinVersionStr provided in global.json is not compatible" - $xcopyMSBuildVersion = $defaultXCopyMSBuildVersion - $vsMajorVersion = $xcopyMSBuildVersion.Split('.')[0] - } - else{ - # If the VS version IS compatible, look for an xcopy msbuild package - # with a version matching VS. - # Note: If this version does not exist, then an explicit version of xcopy msbuild - # can be specified in global.json. This will be required for pre-release versions of msbuild. - $vsMajorVersion = $vsMinVersion.Major - $vsMinorVersion = $vsMinVersion.Minor - $xcopyMSBuildVersion = "$vsMajorVersion.$vsMinorVersion.0" - } - } - - $vsInstallDir = $null - if ($xcopyMSBuildVersion.Trim() -ine "none") { - $vsInstallDir = InitializeXCopyMSBuild $xcopyMSBuildVersion $install - if ($vsInstallDir -eq $null) { - throw "Could not xcopy msbuild. Please check that package 'Microsoft.DotNet.Arcade.MSBuild.Xcopy @ $xcopyMSBuildVersion' exists on feed 'dotnet-eng'." - } - } - if ($vsInstallDir -eq $null) { - throw 'Unable to find Visual Studio that has required version and components installed' - } + throw 'Unable to find Visual Studio that has required version and components installed' } $msbuildVersionDir = if ([int]$vsMajorVersion -lt 16) { "$vsMajorVersion.0" } else { "Current" } @@ -491,38 +459,6 @@ function InitializeVisualStudioEnvironmentVariables([string] $vsInstallDir, [str } } -function InstallXCopyMSBuild([string]$packageVersion) { - return InitializeXCopyMSBuild $packageVersion -install $true -} - -function InitializeXCopyMSBuild([string]$packageVersion, [bool]$install) { - $packageName = 'Microsoft.DotNet.Arcade.MSBuild.Xcopy' - $packageDir = Join-Path $ToolsDir "msbuild\$packageVersion" - $packagePath = Join-Path $packageDir "$packageName.$packageVersion.nupkg" - - if (!(Test-Path $packageDir)) { - if (!$install) { - return $null - } - - Create-Directory $packageDir - - Write-Host "Downloading $packageName $packageVersion" - $ProgressPreference = 'SilentlyContinue' # Don't display the console progress UI - it's a huge perf hit - Retry({ - Invoke-WebRequest "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/flat2/$packageName/$packageVersion/$packageName.$packageVersion.nupkg" -UseBasicParsing -OutFile $packagePath - }) - - if (!(Test-Path $packagePath)) { - Write-PipelineTelemetryError -Category 'InitializeToolset' -Message "See https://dev.azure.com/dnceng/internal/_wiki/wikis/DNCEng%20Services%20Wiki/1074/Updating-Microsoft.DotNet.Arcade.MSBuild.Xcopy-WAS-RoslynTools.MSBuild-(xcopy-msbuild)-generation?anchor=troubleshooting for help troubleshooting issues with XCopy MSBuild" - throw - } - Unzip $packagePath $packageDir - } - - return Join-Path $packageDir 'tools' -} - # # Locates Visual Studio instance that meets the minimal requirements specified by tools.vs object in global.json. # @@ -592,6 +528,11 @@ function LocateVisualStudio([object]$vsRequirements = $null){ return $null } + if ($null -eq $vsInfo -or $vsInfo.Count -eq 0) { + throw "No instance of Visual Studio meeting the requirements specified was found. Requirements: $($args -join ' ')" + return $null + } + # use first matching instance return $vsInfo[0] } @@ -627,7 +568,7 @@ function InitializeBuildTool() { $buildTool = @{ Path = $dotnetPath; Command = 'msbuild'; Tool = 'dotnet'; Framework = 'net' } } elseif ($msbuildEngine -eq "vs") { try { - $msbuildPath = InitializeVisualStudioMSBuild -install:$restore + $msbuildPath = InitializeVisualStudioMSBuild } catch { Write-PipelineTelemetryError -Category 'InitializeToolset' -Message $_ ExitWithExitCode 1 @@ -836,6 +777,10 @@ function MSBuild-Core() { $cmdArgs += ' /p:TreatWarningsAsErrors=false' } + if ($warnAsError -and $warnNotAsError) { + $cmdArgs += " /warnnotaserror:$warnNotAsError /p:AdditionalWarningsNotAsErrors=$warnNotAsError" + } + foreach ($arg in $args) { if ($null -ne $arg -and $arg.Trim() -ne "") { if ($arg.EndsWith('\')) { diff --git a/eng/common/tools.sh b/eng/common/tools.sh index 1b296f646c2..1e37fd95b21 100755 --- a/eng/common/tools.sh +++ b/eng/common/tools.sh @@ -52,6 +52,9 @@ fi # Configures warning treatment in msbuild. warn_as_error=${warn_as_error:-true} +# Specifies semi-colon delimited list of warning codes that should not be treated as errors. +warn_not_as_error=${warn_not_as_error:-''} + # True to attempt using .NET Core already that meets requirements specified in global.json # installed on the machine instead of downloading one. use_installed_dotnet_cli=${use_installed_dotnet_cli:-true} @@ -115,9 +118,6 @@ function InitializeDotNetCli { local install=$1 - # Don't resolve runtime, shared framework, or SDK from other locations to ensure build determinism - export DOTNET_MULTILEVEL_LOOKUP=0 - # Disable first run since we want to control all package sources export DOTNET_NOLOGO=1 @@ -148,7 +148,11 @@ function InitializeDotNetCli { if [[ $global_json_has_runtimes == false && -n "${DOTNET_INSTALL_DIR:-}" && -d "$DOTNET_INSTALL_DIR/sdk/$dotnet_sdk_version" ]]; then dotnet_root="$DOTNET_INSTALL_DIR" else - dotnet_root="${repo_root}.dotnet" + if [[ -n "${DOTNET_GLOBAL_INSTALL_DIR:-}" ]]; then + dotnet_root="$DOTNET_GLOBAL_INSTALL_DIR" + else + dotnet_root="${repo_root}.dotnet" + fi export DOTNET_INSTALL_DIR="$dotnet_root" @@ -166,7 +170,6 @@ function InitializeDotNetCli { # build steps from using anything other than what we've downloaded. Write-PipelinePrependPath -path "$dotnet_root" - Write-PipelineSetVariable -name "DOTNET_MULTILEVEL_LOOKUP" -value "0" Write-PipelineSetVariable -name "DOTNET_NOLOGO" -value "1" # return value @@ -188,6 +191,8 @@ function InstallDotNet { local version=$2 local runtime=$4 + # For performance this check is duplicated in src/Microsoft.DotNet.Arcade.Sdk/src/InstallDotNetCore.cs + # if you are making changes here, consider if you need to make changes there as well. local dotnetVersionLabel="'$runtime v$version'" if [[ -n "${4:-}" ]] && [ "$4" != 'sdk' ]; then runtimePath="$root" @@ -532,7 +537,12 @@ function MSBuild-Core { mt_switch="-mt" fi - RunBuildTool "$_InitializeBuildToolCommand" /m /nologo /clp:Summary /v:$verbosity /nr:$node_reuse $warnaserror_switch $mt_switch /p:TreatWarningsAsErrors=$warn_as_error /p:ContinuousIntegrationBuild=$ci "$@" + local warnnotaserror_switch="" + if [[ -n "$warn_not_as_error" && "$warn_as_error" == true ]]; then + warnnotaserror_switch="/warnnotaserror:$warn_not_as_error /p:AdditionalWarningsNotAsErrors=$warn_not_as_error" + fi + + RunBuildTool "$_InitializeBuildToolCommand" /m /nologo /clp:Summary /v:$verbosity /nr:$node_reuse $warnaserror_switch $mt_switch $warnnotaserror_switch /p:TreatWarningsAsErrors=$warn_as_error /p:ContinuousIntegrationBuild=$ci "$@" } function GetDarc { diff --git a/global.json b/global.json index bd485523f54..ab623d776cd 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "10.0.105", + "version": "11.0.100-preview.3.26170.106", "allowPrerelease": true, "paths": [ ".dotnet", @@ -12,7 +12,7 @@ "runner": "Microsoft.Testing.Platform" }, "tools": { - "dotnet": "10.0.105", + "dotnet": "11.0.100-preview.3.26170.106", "vs": { "version": "18.0", "components": [ @@ -22,7 +22,7 @@ "xcopy-msbuild": "18.0.0" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.26208.4", + "Microsoft.DotNet.Arcade.Sdk": "11.0.0-beta.26211.1", "Microsoft.DotNet.Helix.Sdk": "8.0.0-beta.23255.2" } } diff --git a/src/Compiler/AbstractIL/il.fs b/src/Compiler/AbstractIL/il.fs index b620ee8e7e5..8e836ef548e 100644 --- a/src/Compiler/AbstractIL/il.fs +++ b/src/Compiler/AbstractIL/il.fs @@ -3375,6 +3375,15 @@ let mkILSimpleTypar nm = MetadataIndex = NoMetadataIdx } +let stripILGenericParamConstraints (gp: ILGenericParameterDef) = + { gp with + Constraints = [] + HasReferenceTypeConstraint = false + HasNotNullableValueTypeConstraint = false + HasDefaultConstructorConstraint = false + HasAllowsRefStruct = false + } + let genericParamOfGenericActual (_ga: ILType) = mkILSimpleTypar "T" let mkILFormalTypars (x: ILGenericArgsList) = List.map genericParamOfGenericActual x diff --git a/src/Compiler/AbstractIL/il.fsi b/src/Compiler/AbstractIL/il.fsi index 2071b8658e4..3539e235518 100644 --- a/src/Compiler/AbstractIL/il.fsi +++ b/src/Compiler/AbstractIL/il.fsi @@ -2096,6 +2096,7 @@ val internal mkILFormalNamedTy: ILBoxity -> ILTypeRef -> ILGenericParameterDef l val internal mkILFormalTypars: ILType list -> ILGenericParameterDefs val internal mkILFormalGenericArgs: int -> ILGenericParameterDefs -> ILGenericArgsList val internal mkILSimpleTypar: string -> ILGenericParameterDef +val internal stripILGenericParamConstraints: ILGenericParameterDef -> ILGenericParameterDef /// Make custom attributes. val internal mkILCustomAttribMethRef: diff --git a/src/Compiler/Checking/CheckDeclarations.fs b/src/Compiler/Checking/CheckDeclarations.fs index f425f49dafc..3d80f056de0 100644 --- a/src/Compiler/Checking/CheckDeclarations.fs +++ b/src/Compiler/Checking/CheckDeclarations.fs @@ -4418,7 +4418,7 @@ module TcDeclarations = // Only the keep the field-targeted attributes let attribs = attribs |> List.filter (fun a -> match a.Target with Some t when t.idText = "field" -> true | _ -> false) let mLetPortion = synExpr.Range - let fldId = ident (CompilerGeneratedName id.idText, mLetPortion) + let fldId = ident (CompilerGeneratedName id.idText, mLetPortion.MakeSynthetic()) let headPat = SynPat.LongIdent (SynLongIdent([fldId], [], [None]), None, Some noInferredTypars, SynArgPats.Pats [], None, mLetPortion) let retInfo = match tyOpt with None -> None | Some ty -> Some (None, SynReturnInfo((ty, SynInfo.unnamedRetVal), ty.Range)) let isMutable = @@ -4446,7 +4446,7 @@ module TcDeclarations = let mMemberPortion = id.idRange // Only the keep the non-field-targeted attributes let attribs = attribs |> List.filter (fun a -> match a.Target with Some t when t.idText = "field" -> false | _ -> true) - let fldId = ident (CompilerGeneratedName id.idText, mMemberPortion) + let fldId = ident (CompilerGeneratedName id.idText, mMemberPortion.MakeSynthetic()) let headPatIds = if isStatic then [id] else [ident ("__", mMemberPortion);id] let headPat = SynPat.LongIdent (SynLongIdent(headPatIds, [], List.replicate headPatIds.Length None), None, Some noInferredTypars, SynArgPats.Pats [], None, mMemberPortion) let memberFlags = { memberFlags with GetterOrSetterIsCompilerGenerated = true } @@ -4475,7 +4475,7 @@ module TcDeclarations = | SynMemberKind.PropertySet | SynMemberKind.PropertyGetSet -> let setter = - let vId = ident("v", mMemberPortion) + let vId = ident("v", mMemberPortion.MakeSynthetic()) let headPat = SynPat.LongIdent (SynLongIdent(headPatIds, [], List.replicate headPatIds.Length None), None, Some noInferredTypars, SynArgPats.Pats [mkSynPatVar None vId], None, mMemberPortion) let rhsExpr = mkSynAssign (SynExpr.Ident fldId) (SynExpr.Ident vId) let binding = mkSynBinding (xmlDoc, headPat) (setterAccess, false, false, mMemberPortion, DebugPointAtBinding.NoneAtInvisible, None, rhsExpr, rhsExpr.Range, [], [], Some memberFlagsForSet, SynBindingTrivia.Zero) diff --git a/src/Compiler/Checking/Expressions/CheckExpressions.fs b/src/Compiler/Checking/Expressions/CheckExpressions.fs index 5a6bbf619fa..ff9f3c89bf0 100644 --- a/src/Compiler/Checking/Expressions/CheckExpressions.fs +++ b/src/Compiler/Checking/Expressions/CheckExpressions.fs @@ -1690,6 +1690,11 @@ let GeneralizeVal (cenv: cenv) denv enclosingDeclaredTypars generalizedTyparsFor // This is just about the only place we form a GeneralizedType let tyScheme = GeneralizedType(generalizedTypars, ty) + // Decouple SRTP solution ref cells so codegen mutations don't bleed across FSI submissions. + // In compiled code, pickling naturally creates fresh cells. See #12386. + if cenv.g.isInteractive then + decoupleTraitSolutions generalizedTypars + PrelimVal2(id, tyScheme, prelimValReprInfo, memberInfoOpt, isMutable, inlineFlag, baseOrThis, argAttribs, vis, isCompGen, hasDeclaredTypars) let GeneralizeVals (cenv: cenv) denv enclosingDeclaredTypars generalizedTypars types = diff --git a/src/Compiler/Checking/PostInferenceChecks.fs b/src/Compiler/Checking/PostInferenceChecks.fs index 09e95d5bbf1..7924394c743 100644 --- a/src/Compiler/Checking/PostInferenceChecks.fs +++ b/src/Compiler/Checking/PostInferenceChecks.fs @@ -2036,8 +2036,19 @@ and CheckAttribs cenv env (attribs: Attribs) = |> Seq.filter (fun (_, count) -> count > 1) |> Seq.map fst |> Seq.toList - // Filter for allowMultiple = false - |> List.filter (fun (tcref, _, m) -> TryFindAttributeUsageAttribute cenv.g m tcref <> Some true) + // Filter for allowMultiple = false, walking the inheritance chain to find AttributeUsage + |> List.filter (fun (tcref, _, m) -> + let rec allowsMultiple (tcref: TyconRef) = + match TryFindAttributeUsageAttribute cenv.g m tcref with + | Some res -> res + | None -> + generalizedTyconRef cenv.g tcref + |> GetSuperTypeOfType cenv.g cenv.amap m + |> Option.bind (tryTcrefOfAppTy cenv.g >> ValueOption.toOption) + |> Option.map allowsMultiple + |> Option.defaultValue false + + not (allowsMultiple tcref)) if cenv.reportErrors then for tcref, _, m in duplicates do diff --git a/src/Compiler/CodeGen/EraseClosures.fs b/src/Compiler/CodeGen/EraseClosures.fs index 930e6201bf8..6aef85d40b5 100644 --- a/src/Compiler/CodeGen/EraseClosures.fs +++ b/src/Compiler/CodeGen/EraseClosures.fs @@ -153,7 +153,19 @@ let newIlxPubCloEnv (ilg, addMethodGeneratedAttrs, addFieldGeneratedAttrs, addFi let mkILTyFuncTy cenv = cenv.mkILTyFuncTy +let inline private (|IsVoidPtr|_|) ty = + match ty with + | ILType.Ptr ILType.Void -> true + | _ -> false + +let private fixVoidPtrForGenericArg (ilg: ILGlobals) ty = + match ty with + | IsVoidPtr -> ilg.typ_IntPtr + | _ -> ty + let mkILFuncTy cenv dty rty = + let dty = fixVoidPtrForGenericArg cenv.ilg dty + let rty = fixVoidPtrForGenericArg cenv.ilg rty mkILBoxedTy cenv.tref_Func[0] [ dty; rty ] let mkILCurriedFuncTy cenv dtys rty = @@ -168,6 +180,8 @@ let typ_Func cenv (dtys: ILType list) rty = else mkFuncTypeRef cenv.ilg.fsharpCoreAssemblyScopeRef n + let dtys = dtys |> List.map (fixVoidPtrForGenericArg cenv.ilg) + let rty = fixVoidPtrForGenericArg cenv.ilg rty mkILBoxedTy tref (dtys @ [ rty ]) let rec mkTyOfApps cenv apps = @@ -190,6 +204,8 @@ let mkMethSpecForMultiApp cenv (argTys: ILType list, retTy) = let n = argTys.Length let formalArgTys = List.mapi (fun i _ -> ILType.TypeVar(uint16 i)) argTys let formalRetTy = ILType.TypeVar(uint16 n) + let argTys = argTys |> List.map (fixVoidPtrForGenericArg cenv.ilg) + let retTy = fixVoidPtrForGenericArg cenv.ilg retTy let inst = argTys @ [ retTy ] if n = 1 then @@ -548,12 +564,14 @@ let rec convIlxClosureDef cenv encl (td: ILTypeDef) clo = let convil = convILMethodBody (Some nowCloSpec, boxReturnTy) clo.cloCode.Value + let specializeGenParams = addedGenParams |> List.map stripILGenericParamConstraints + let nowApplyMethDef = mkILGenericVirtualMethod ( "Specialize", ILCallingConv.Instance, ILMemberAccess.Public, - addedGenParams (* method is generic over added ILGenericParameterDefs *) , + specializeGenParams, [], mkILReturn cenv.ilg.typ_Object, MethodBody.IL(notlazy convil) @@ -681,7 +699,17 @@ let rec convIlxClosureDef cenv encl (td: ILTypeDef) clo = else // CASE 2b - Build an Invoke method - let nowEnvParentClass = typ_Func cenv (typesOfILParams nowParams) nowReturnTy + let fixedNowParams = + nowParams + |> List.map (fun (p: ILParameter) -> + { p with + Type = fixVoidPtrForGenericArg cenv.ilg p.Type + }) + + let fixedNowReturnTy = fixVoidPtrForGenericArg cenv.ilg nowReturnTy + + let nowEnvParentClass = + typ_Func cenv (typesOfILParams fixedNowParams) fixedNowReturnTy let cloTypeDef = let convil = convILMethodBody (Some nowCloSpec, None) clo.cloCode.Value @@ -690,8 +718,8 @@ let rec convIlxClosureDef cenv encl (td: ILTypeDef) clo = mkILNonGenericVirtualInstanceMethod ( "Invoke", ILMemberAccess.Public, - nowParams, - mkILReturn nowReturnTy, + fixedNowParams, + mkILReturn fixedNowReturnTy, MethodBody.IL(notlazy convil) ) diff --git a/src/Compiler/CodeGen/IlxGen.fs b/src/Compiler/CodeGen/IlxGen.fs index 6dcb2f92ee8..b76cc30293f 100644 --- a/src/Compiler/CodeGen/IlxGen.fs +++ b/src/Compiler/CodeGen/IlxGen.fs @@ -1398,6 +1398,41 @@ let TryStorageForWitness (_g: TcGlobals) eenv (w: TraitWitnessInfo) = let IsValRefIsDllImport g (vref: ValRef) = ValHasWellKnownAttribute g WellKnownValAttributes.DllImportAttribute vref.Deref +/// Check if a type contains nativeptr with a type parameter from the given set. +/// Used to detect interface implementations that need 'native int' instead of 'T*'. +let hasNativePtrWithTypar (g: TcGlobals) (typars: Typar list) ty = + let rec check ty = + let ty = stripTyEqns g ty + + match ty with + | TType_app(tcref, tinst, _) when tyconRefEq g g.nativeptr_tcr tcref -> + tinst + |> List.exists (fun t -> + match stripTyEqns g t with + | TType_var(tp, _) -> typars |> List.exists (fun tp2 -> tp.Stamp = tp2.Stamp) + | _ -> false) + | TType_app(_, tinst, _) -> tinst |> List.exists check + | TType_fun(d, r, _) -> check d || check r + | TType_tuple(_, tys) -> tys |> List.exists check + | TType_anon(_, tys) -> tys |> List.exists check + | TType_forall(_, t) -> check t + | _ -> false + + check ty + +/// Check if a slot signature requires nativeptr rewriting: the slot has nativeptr<'T> +/// where 'T is a class type parameter and the interface type arguments are concrete. +/// When true, method definitions must use 'native int' instead of 'T*' for the +/// affected params/returns to match the interface slot's IL signature. +let slotSigRequiresNativePtrRewrite (g: TcGlobals) ty (sctps: Typars) (sParams: SlotParam list list) (sRetTy: TType option) = + not sctps.IsEmpty + && (let interfaceTypeArgs = argsOfAppTy g ty + (freeInTypes CollectTypars interfaceTypeArgs).FreeTypars.IsEmpty) + && (sParams + |> List.concat + |> List.exists (fun (TSlotParam(_, paramTy, _, _, _, _)) -> hasNativePtrWithTypar g sctps paramTy) + || sRetTy |> Option.exists (hasNativePtrWithTypar g sctps)) + /// Determine how a top level value is represented, when it is being represented /// as a method. let GetMethodSpecForMemberVal cenv (memberInfo: ValMemberInfo) (vref: ValRef) = @@ -1423,15 +1458,40 @@ let GetMethodSpecForMemberVal cenv (memberInfo: ValMemberInfo) (vref: ValRef) = let ctps, mtps = List.splitAt numParentTypars tps let isCompiledAsInstance = ValRefIsCompiledAsInstanceMember g vref + // When implementing an interface slot with nativeptr<'T> where 'T is an interface + // type parameter and the interface type args are concrete, the interface method + // uses 'native int'. The method def must also use 'native int' to match. + // This applies to both return types and parameter types. + let nativePtrSlotRewriteInfo = + if not (isCtor || cctor) && memberInfo.MemberFlags.IsOverrideOrExplicitImpl then + memberInfo.ImplementedSlotSigs + |> List.tryPick (fun (TSlotSig(_, ty, sctps, _, sParams, sRetTy)) -> + if slotSigRequiresNativePtrRewrite g ty sctps sParams sRetTy then + let slotEnv = TypeReprEnv.Empty.ForTypars sctps + let slotParamTys = sParams |> List.concat |> List.map (fun p -> p.Type) + Some(slotEnv, sctps, slotParamTys, sRetTy) + else + None) + else + None + let ilActualRetTy = let ilRetTy = GenReturnType cenv m tyenvUnderTypars returnTy - if isCtor || cctor then ILType.Void else ilRetTy + + if isCtor || cctor then + ILType.Void + else + match nativePtrSlotRewriteInfo with + | Some(slotEnv, sctps, _, sRetTy) when sRetTy |> Option.exists (hasNativePtrWithTypar g sctps) -> + GenReturnType cenv m slotEnv sRetTy + | _ -> ilRetTy let ilTy = GenType cenv m tyenvUnderTypars (mkWoNullAppTy parentTcref (List.map mkTyparTy ctps)) let nm = vref.CompiledName g.CompilerGlobalState + // Instance methods: validate 'this' type and remove it from the arg list if isCompiledAsInstance || isCtor then // Find the 'this' argument type if any let thisTy, flatArgInfos = @@ -1470,41 +1530,40 @@ let GetMethodSpecForMemberVal cenv (memberInfo: ValMemberInfo) (vref: ValRef) = ) )) + flatArgInfos + else + flatArgInfos + // Common: generate param types, method spec, witness spec + |> fun flatArgInfos -> + let methodArgTys, paramInfos = List.unzip flatArgInfos let isSlotSig = memberInfo.MemberFlags.IsDispatchSlot || memberInfo.MemberFlags.IsOverrideOrExplicitImpl - let ilMethodArgTys = GenParamTypes cenv m tyenvUnderTypars isSlotSig methodArgTys - let ilMethodInst = GenTypeArgs cenv m tyenvUnderTypars (List.map mkTyparTy mtps) - - let mspec = - mkILInstanceMethSpecInTy (ilTy, nm, ilMethodArgTys, ilActualRetTy, ilMethodInst) - - let mspecW = - if not g.generateWitnesses || witnessInfos.IsEmpty then - mspec - else - let ilWitnessArgTys = - GenTypes cenv m tyenvUnderTypars (GenWitnessTys g witnessInfos) - - let nmW = ExtraWitnessMethodName nm - mkILInstanceMethSpecInTy (ilTy, nmW, ilWitnessArgTys @ ilMethodArgTys, ilActualRetTy, ilMethodInst) - - mspec, mspecW, ctps, mtps, curriedArgInfos, paramInfos, retInfo, witnessInfos, methodArgTys, returnTy - else - let methodArgTys, paramInfos = List.unzip flatArgInfos + let ilMethodArgTys = + let ilArgTys = GenParamTypes cenv m tyenvUnderTypars isSlotSig methodArgTys - let isSlotSig = - memberInfo.MemberFlags.IsDispatchSlot - || memberInfo.MemberFlags.IsOverrideOrExplicitImpl + match nativePtrSlotRewriteInfo with + | Some(slotEnv, sctps, slotParamTys, _) when slotParamTys.Length = ilArgTys.Length -> + (ilArgTys, slotParamTys) + ||> List.map2 (fun ilArgTy slotParamTy -> + if hasNativePtrWithTypar g sctps slotParamTy then + GenParamType cenv m slotEnv true slotParamTy + else + ilArgTy) + | _ -> ilArgTys - let ilMethodArgTys = GenParamTypes cenv m tyenvUnderTypars isSlotSig methodArgTys let ilMethodInst = GenTypeArgs cenv m tyenvUnderTypars (List.map mkTyparTy mtps) - let mspec = - mkILStaticMethSpecInTy (ilTy, nm, ilMethodArgTys, ilActualRetTy, ilMethodInst) + let mkMethSpec = + if isCompiledAsInstance || isCtor then + mkILInstanceMethSpecInTy + else + mkILStaticMethSpecInTy + + let mspec = mkMethSpec (ilTy, nm, ilMethodArgTys, ilActualRetTy, ilMethodInst) let mspecW = if not g.generateWitnesses || witnessInfos.IsEmpty then @@ -1514,7 +1573,7 @@ let GetMethodSpecForMemberVal cenv (memberInfo: ValMemberInfo) (vref: ValRef) = GenTypes cenv m tyenvUnderTypars (GenWitnessTys g witnessInfos) let nmW = ExtraWitnessMethodName nm - mkILStaticMethSpecInTy (ilTy, nmW, ilWitnessArgTys @ ilMethodArgTys, ilActualRetTy, ilMethodInst) + mkMethSpec (ilTy, nmW, ilWitnessArgTys @ ilMethodArgTys, ilActualRetTy, ilMethodInst) mspec, mspecW, ctps, mtps, curriedArgInfos, paramInfos, retInfo, witnessInfos, methodArgTys, returnTy @@ -2516,6 +2575,8 @@ type CodeGenBuffer(m: range, mgbuf: AssemblyBuilder, methodName, alreadyUsedArgs let mutable hasDebugPoints = false let mutable anyDocument = None // we collect an arbitrary document in order to emit the header FeeFee if needed + let mutable hasStackAllocatedLocals = false + let codeLabelToPC: Dictionary = Dictionary<_, _>(10) let codeLabelToCodeLabel: Dictionary = @@ -2574,11 +2635,19 @@ type CodeGenBuffer(m: range, mgbuf: AssemblyBuilder, methodName, alreadyUsedArgs member cgbuf.EmitInstr(pops, pushes, i) = cgbuf.DoPops pops cgbuf.DoPushes pushes + + if i = I_localloc then + hasStackAllocatedLocals <- true + codebuf.Add i member cgbuf.EmitInstrs(pops, pushes, is) = cgbuf.DoPops pops cgbuf.DoPushes pushes + + if is |> List.exists (fun i -> i = I_localloc) then + hasStackAllocatedLocals <- true + is |> List.iter codebuf.Add member private _.EnsureNopBetweenDebugPoints() = @@ -2711,6 +2780,8 @@ type CodeGenBuffer(m: range, mgbuf: AssemblyBuilder, methodName, alreadyUsedArgs member _.HasPinnedLocals() = locals |> Seq.exists (fun (_, _, isFixed, _) -> isFixed) + member _.HasStackAllocatedLocals() = hasStackAllocatedLocals + member _.Close() = let instrs = codebuf.ToArray() @@ -3341,32 +3412,50 @@ and GenConstant cenv cgbuf eenv (c, m, ty) sequel = match TryEliminateDesugaredConstants g m c with | Some e -> GenExpr cenv cgbuf eenv e Continue | None -> - let emitInt64Constant i = + let needsBoxingToTargetTy = + (match ilTy with + | ILType.Value _ -> false + | _ -> true) + + // Wraps an emitter: calls it, then boxes if target type is not a value type (e.g. literal upcast to obj). + let inline emitAndBoxIfNeeded emitter uty arg = + emitter uty arg + + if needsBoxingToTargetTy then + CG.EmitInstr cgbuf (pop 1) (Push [ ilTy ]) (I_box uty) + + let emitInt64Constant uty i = // see https://github.com/dotnet/fsharp/pull/3620 // and https://github.com/dotnet/fsharp/issue/8683 // and https://github.com/dotnet/roslyn/blob/98f12bb/src/Compilers/Core/Portable/CodeGen/ILBuilderEmit.cs#L679 if i >= int64 Int32.MinValue && i <= int64 Int32.MaxValue then - CG.EmitInstrs cgbuf (pop 0) (Push [ ilTy ]) [ mkLdcInt32 (int32 i); AI_conv DT_I8 ] + CG.EmitInstrs cgbuf (pop 0) (Push [ uty ]) [ mkLdcInt32 (int32 i); AI_conv DT_I8 ] elif i >= int64 UInt32.MinValue && i <= int64 UInt32.MaxValue then - CG.EmitInstrs cgbuf (pop 0) (Push [ ilTy ]) [ mkLdcInt32 (int32 i); AI_conv DT_U8 ] + CG.EmitInstrs cgbuf (pop 0) (Push [ uty ]) [ mkLdcInt32 (int32 i); AI_conv DT_U8 ] else - CG.EmitInstr cgbuf (pop 0) (Push [ ilTy ]) (iLdcInt64 i) + CG.EmitInstr cgbuf (pop 0) (Push [ uty ]) (iLdcInt64 i) + + let emitConst uty instr = + CG.EmitInstr cgbuf (pop 0) (Push [ uty ]) instr + + let emitConstI uty instrs = + CG.EmitInstrs cgbuf (pop 0) (Push [ uty ]) instrs match c with - | Const.Bool b -> CG.EmitInstr cgbuf (pop 0) (Push [ g.ilg.typ_Bool ]) (mkLdcInt32 (if b then 1 else 0)) - | Const.SByte i -> CG.EmitInstr cgbuf (pop 0) (Push [ ilTy ]) (mkLdcInt32 (int32 i)) - | Const.Int16 i -> CG.EmitInstr cgbuf (pop 0) (Push [ ilTy ]) (mkLdcInt32 (int32 i)) - | Const.Int32 i -> CG.EmitInstr cgbuf (pop 0) (Push [ ilTy ]) (mkLdcInt32 i) - | Const.Int64 i -> emitInt64Constant i - | Const.IntPtr i -> CG.EmitInstrs cgbuf (pop 0) (Push [ ilTy ]) [ iLdcInt64 i; AI_conv DT_I ] - | Const.Byte i -> CG.EmitInstr cgbuf (pop 0) (Push [ ilTy ]) (mkLdcInt32 (int32 i)) - | Const.UInt16 i -> CG.EmitInstr cgbuf (pop 0) (Push [ ilTy ]) (mkLdcInt32 (int32 i)) - | Const.UInt32 i -> CG.EmitInstr cgbuf (pop 0) (Push [ ilTy ]) (mkLdcInt32 (int32 i)) - | Const.UInt64 i -> emitInt64Constant (int64 i) - | Const.UIntPtr i -> CG.EmitInstrs cgbuf (pop 0) (Push [ ilTy ]) [ iLdcInt64 (int64 i); AI_conv DT_U ] - | Const.Double f -> CG.EmitInstr cgbuf (pop 0) (Push [ ilTy ]) (AI_ldc(DT_R8, ILConst.R8 f)) - | Const.Single f -> CG.EmitInstr cgbuf (pop 0) (Push [ ilTy ]) (AI_ldc(DT_R4, ILConst.R4 f)) - | Const.Char c -> CG.EmitInstr cgbuf (pop 0) (Push [ ilTy ]) (mkLdcInt32 (int c)) + | Const.Bool b -> emitAndBoxIfNeeded emitConst g.ilg.typ_Bool (mkLdcInt32 (if b then 1 else 0)) + | Const.SByte i -> emitAndBoxIfNeeded emitConst g.ilg.typ_SByte (mkLdcInt32 (int32 i)) + | Const.Int16 i -> emitAndBoxIfNeeded emitConst g.ilg.typ_Int16 (mkLdcInt32 (int32 i)) + | Const.Int32 i -> emitAndBoxIfNeeded emitConst g.ilg.typ_Int32 (mkLdcInt32 i) + | Const.Int64 i -> emitAndBoxIfNeeded emitInt64Constant g.ilg.typ_Int64 i + | Const.IntPtr i -> emitAndBoxIfNeeded emitConstI g.ilg.typ_IntPtr [ iLdcInt64 i; AI_conv DT_I ] + | Const.Byte i -> emitAndBoxIfNeeded emitConst g.ilg.typ_Byte (mkLdcInt32 (int32 i)) + | Const.UInt16 i -> emitAndBoxIfNeeded emitConst g.ilg.typ_UInt16 (mkLdcInt32 (int32 i)) + | Const.UInt32 i -> emitAndBoxIfNeeded emitConst g.ilg.typ_UInt32 (mkLdcInt32 (int32 i)) + | Const.UInt64 i -> emitAndBoxIfNeeded emitInt64Constant g.ilg.typ_UInt64 (int64 i) + | Const.UIntPtr i -> emitAndBoxIfNeeded emitConstI g.ilg.typ_UIntPtr [ iLdcInt64 (int64 i); AI_conv DT_U ] + | Const.Double f -> emitAndBoxIfNeeded emitConst g.ilg.typ_Double (AI_ldc(DT_R8, ILConst.R8 f)) + | Const.Single f -> emitAndBoxIfNeeded emitConst g.ilg.typ_Single (AI_ldc(DT_R4, ILConst.R4 f)) + | Const.Char c -> emitAndBoxIfNeeded emitConst g.ilg.typ_Char (mkLdcInt32 (int c)) | Const.String s -> GenString cenv cgbuf s | Const.Unit -> GenUnit cenv eenv m cgbuf | Const.Zero -> GenDefaultValue cenv cgbuf eenv (ty, m) @@ -4559,6 +4648,7 @@ and CanTailcall // Can't tailcall with a .NET 2.0 generic constrained call since it involves a byref // Can't tailcall when there are pinned locals since the stack frame must remain alive let hasPinnedLocals = cgbuf.HasPinnedLocals() + let hasStackAllocatedLocals = cgbuf.HasStackAllocatedLocals() if not hasStructObjArg @@ -4569,6 +4659,7 @@ and CanTailcall && not isSelfInit && not makesNoCriticalTailcalls && not hasPinnedLocals + && not hasStackAllocatedLocals && // We can tailcall even if we need to generate "unit", as long as we're about to throw the value away anyway as par of the return. @@ -5907,14 +5998,35 @@ and GenActualSlotsig methTyparsOfOverridingMethod (methodParams: Val list) = + let g = cenv.g let ilSlotParams = List.concat ilSlotParams let instForSlotSig = - mkTyparInst (ctps @ mtps) (argsOfAppTy cenv.g ty @ generalizeTypars methTyparsOfOverridingMethod) + mkTyparInst (ctps @ mtps) (argsOfAppTy g ty @ generalizeTypars methTyparsOfOverridingMethod) + + let slotHasNativePtrWithCtps = + slotSigRequiresNativePtrRewrite g ty ctps [ ilSlotParams ] ilSlotRetTy + + let eenvForSlotGen = + if slotHasNativePtrWithCtps then + EnvForTypars ctps eenv + else + eenv + + // When the slot has nativeptr with concrete interface type args, don't substitute + // the class type params (ctps) - only substitute method type params (mtps). + // This keeps nativeptr<'T> unsubstituted so it generates 'native int' in IL, + // matching the interface method's signature. Without this, nativeptr<'T> would be + // substituted to nativeptr which generates 'T*', causing a TypeLoadException. + let instForSlotSigGen = + if slotHasNativePtrWithCtps then + mkTyparInst mtps (generalizeTypars methTyparsOfOverridingMethod) + else + instForSlotSig let ilParams = ilSlotParams - |> List.map (instSlotParam instForSlotSig >> GenSlotParam m cenv eenv) + |> List.map (instSlotParam instForSlotSigGen >> GenSlotParam m cenv eenvForSlotGen) // Use the better names if available let ilParams = @@ -5925,7 +6037,7 @@ and GenActualSlotsig ilParams let ilRetTy = - GenReturnType cenv m eenv.tyenv (Option.map (instType instForSlotSig) ilSlotRetTy) + GenReturnType cenv m eenvForSlotGen.tyenv (Option.map (instType instForSlotSigGen) ilSlotRetTy) let iLRet = mkILReturn ilRetTy @@ -5933,7 +6045,7 @@ and GenActualSlotsig match ilSlotRetTy with | None -> iLRet | Some t -> - match GenAdditionalAttributesForTy cenv.g t with + match GenAdditionalAttributesForTy g t with | [] -> iLRet | attrs -> iLRet.WithCustomAttrs(mkILCustomAttrs attrs) diff --git a/src/Compiler/Service/ServiceParseTreeWalk.fs b/src/Compiler/Service/ServiceParseTreeWalk.fs index 51e2b8ac43b..4a1177b7b26 100644 --- a/src/Compiler/Service/ServiceParseTreeWalk.fs +++ b/src/Compiler/Service/ServiceParseTreeWalk.fs @@ -801,6 +801,8 @@ module SyntaxTraversal = | None -> traverseSynType path ty | x -> x | SynPat.QuoteExpr(expr, _) -> traverseSynExpr path expr + | SynPat.IsInst(ty, _) -> traverseSynType path ty + | SynPat.FromParseError(p, _) -> traversePat path p | _ -> None visitor.VisitPat(origPath, defaultTraverse, pat) diff --git a/src/Compiler/TypedTree/TypedTree.fs b/src/Compiler/TypedTree/TypedTree.fs index 995071138b5..733b269b588 100644 --- a/src/Compiler/TypedTree/TypedTree.fs +++ b/src/Compiler/TypedTree/TypedTree.fs @@ -2631,6 +2631,10 @@ type TraitConstraintInfo = with get() = (let (TTrait(solution = sln)) = x in sln.Value) and set v = (let (TTrait(solution = sln)) = x in sln.Value <- v) + member x.CloneWithFreshSolution() = + let (TTrait(a, b, c, d, e, f, sln)) = x + TTrait(a, b, c, d, e, f, ref sln.Value) + member x.WithMemberKind(kind) = (let (TTrait(a, b, c, d, e, f, g)) = x in TTrait(a, b, { c with MemberKind=kind }, d, e, f, g)) member x.WithSupportTypes(tys) = (let (TTrait(_, b, c, d, e, f, g)) = x in TTrait(tys, b, c, d, e, f, g)) diff --git a/src/Compiler/TypedTree/TypedTree.fsi b/src/Compiler/TypedTree/TypedTree.fsi index 0cd4bfd2305..7fbe446641a 100644 --- a/src/Compiler/TypedTree/TypedTree.fsi +++ b/src/Compiler/TypedTree/TypedTree.fsi @@ -1791,6 +1791,8 @@ type TraitConstraintInfo = /// Get or set the solution of the member constraint during inference member Solution: TraitConstraintSln option with get, set + member CloneWithFreshSolution: unit -> TraitConstraintInfo + /// The member kind is irrelevant to the logical properties of a trait. However it adjusts /// the extension property MemberDisplayNameCore member WithMemberKind: SynMemberKind -> TraitConstraintInfo diff --git a/src/Compiler/TypedTree/TypedTreeOps.Attributes.fs b/src/Compiler/TypedTree/TypedTreeOps.Attributes.fs index 488cc8e2521..a57c6e23804 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.Attributes.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.Attributes.fs @@ -812,30 +812,29 @@ module internal AttributeHelpers = | [ Some(:? bool as v: obj) ], _ -> Some v | _ -> None) - /// Try to find the resolved attributeusage for an type by walking its inheritance tree and picking the correct attribute usage value + /// Try to find the AllowMultiple value of the AttributeUsage attribute on a type definition. let TryFindAttributeUsageAttribute g m tcref = - [| yield tcref; yield! supersOfTyconRef tcref |] - |> Array.tryPick (fun tcref -> - TryBindTyconRefAttribute - g - m - g.attrib_AttributeUsageAttribute - tcref - (fun (_, named) -> - named - |> List.tryPick (function - | "AllowMultiple", _, _, ILAttribElem.Bool res -> Some res - | _ -> None)) - (fun (Attrib(_, _, _, named, _, _, _)) -> - named - |> List.tryPick (function - | AttribNamedArg("AllowMultiple", _, _, AttribBoolArg res) -> Some res - | _ -> None)) - (fun (_, named) -> - named - |> List.tryPick (function - | "AllowMultiple", Some(:? bool as res: obj) -> Some res - | _ -> None))) + tryBindTyconRefAttributeCore + g + m + (ValueSome WellKnownILAttributes.AttributeUsageAttribute) + g.attrib_AttributeUsageAttribute + tcref + (fun (_, named) -> + named + |> List.tryPick (function + | "AllowMultiple", _, _, ILAttribElem.Bool res -> Some res + | _ -> None)) + (fun (Attrib(_, _, _, named, _, _, _)) -> + named + |> List.tryPick (function + | AttribNamedArg("AllowMultiple", _, _, AttribBoolArg res) -> Some res + | _ -> None)) + (fun (_, named) -> + named + |> List.tryPick (function + | "AllowMultiple", Some(:? bool as res: obj) -> Some res + | _ -> None)) /// Try to find a specific attribute on a type definition, where the attribute accepts a string argument. /// diff --git a/src/Compiler/TypedTree/TypedTreeOps.Attributes.fsi b/src/Compiler/TypedTree/TypedTreeOps.Attributes.fsi index 76096c3d65e..8fc3910354a 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.Attributes.fsi +++ b/src/Compiler/TypedTree/TypedTreeOps.Attributes.fsi @@ -194,7 +194,7 @@ module internal AttributeHelpers = /// Check if a TyconRef has AllowNullLiteralAttribute, returning Some true/Some false/None. val TyconRefAllowsNull: g: TcGlobals -> tcref: TyconRef -> bool option - /// Try to find the AttributeUsage attribute, looking for the value of the AllowMultiple named parameter + /// Try to find the AllowMultiple value of the AttributeUsage attribute on a type definition. val TryFindAttributeUsageAttribute: TcGlobals -> range -> TyconRef -> bool option val (|AttribBitwiseOrExpr|_|): TcGlobals -> Expr -> (Expr * Expr) voption diff --git a/src/Compiler/TypedTree/TypedTreeOps.FreeVars.fs b/src/Compiler/TypedTree/TypedTreeOps.FreeVars.fs index 2c84aeb7b76..92649150c9d 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.FreeVars.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.FreeVars.fs @@ -1542,11 +1542,3 @@ module internal MemberRepresentation = match tycon.TypeContents.tcaug_super with | None -> g.obj_ty_noNulls | Some ty -> ty - - /// walk a TyconRef's inheritance tree, yielding any parent types as an array - let supersOfTyconRef (tcref: TyconRef) = - tcref - |> Array.unfold (fun tcref -> - match tcref.TypeContents.tcaug_super with - | Some(TType_app(sup, _, _)) -> Some(sup, sup) - | _ -> None) diff --git a/src/Compiler/TypedTree/TypedTreeOps.FreeVars.fsi b/src/Compiler/TypedTree/TypedTreeOps.FreeVars.fsi index e7e37aef6e3..1787715a6e3 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.FreeVars.fsi +++ b/src/Compiler/TypedTree/TypedTreeOps.FreeVars.fsi @@ -357,9 +357,6 @@ module internal MemberRepresentation = val superOfTycon: TcGlobals -> Tycon -> TType - /// walk a TyconRef's inheritance tree, yielding any parent types as an array - val supersOfTyconRef: TyconRef -> TyconRef array - val GetTraitConstraintInfosOfTypars: TcGlobals -> Typars -> TraitConstraintInfo list val GetTraitWitnessInfosOfTypars: TcGlobals -> numParentTypars: int -> typars: Typars -> TraitWitnessInfos diff --git a/src/Compiler/TypedTree/TypedTreeOps.Remap.fs b/src/Compiler/TypedTree/TypedTreeOps.Remap.fs index b598accceff..605b189ce17 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.Remap.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.Remap.fs @@ -497,6 +497,20 @@ module internal TypeRemapping = let copySlotSig ss = remapSlotSig (fun _ -> []) Remap.Empty ss + /// Decouple SRTP constraint solution ref cells on typars from any shared expression-tree nodes. + /// In FSI, codegen mutates shared TTrait solution cells after typechecking; decoupling at + /// generalization prevents stale solutions from bleeding into subsequent submissions. See #12386. + let decoupleTraitSolutions (typars: Typars) = + for tp in typars do + tp.SetConstraints( + tp.Constraints + |> List.map (fun cx -> + match cx with + | TyparConstraint.MayResolveMember(traitInfo, m) -> + TyparConstraint.MayResolveMember(traitInfo.CloneWithFreshSolution(), m) + | c -> c) + ) + let mkTyparToTyparRenaming tpsorig tps = let tinst = generalizeTypars tps mkTyparInst tpsorig tinst, tinst diff --git a/src/Compiler/TypedTree/TypedTreeOps.Remap.fsi b/src/Compiler/TypedTree/TypedTreeOps.Remap.fsi index 090d07beeb1..04cc615a670 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.Remap.fsi +++ b/src/Compiler/TypedTree/TypedTreeOps.Remap.fsi @@ -193,6 +193,9 @@ module internal TypeRemapping = /// Copy a method slot signature, including new generic type parameters if the slot signature represents a generic method val copySlotSig: SlotSig -> SlotSig + /// Decouple SRTP constraint solution ref cells on typars from shared expression-tree nodes. + val decoupleTraitSolutions: Typars -> unit + val mkTyparToTyparRenaming: Typars -> Typars -> TyparInstantiation * TTypes val mkTyconInst: Tycon -> TypeInst -> TyparInstantiation diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/ParserErrors.fs b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/ParserErrors.fs new file mode 100644 index 00000000000..15d896680d5 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/ParserErrors.fs @@ -0,0 +1,117 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace CompilerOptions.Fsc + +open Xunit +open FSharp.Test.Compiler + +/// Tests for compiler options parser error handling across many options +module ParserErrors = + + // ================================================================= + // Missing argument — options that require a parameter (error 224) + // ================================================================= + + [] + [] + [] + [] + [] + [] + [] + [] + [] + [] + [] + [] + [] + [] + [] + [] + [] + [] + [] + [] + let ``options requiring a parameter produce error 224 when missing`` (option: string) = + Fs """module M""" + |> withOptions [option] + |> compile + |> shouldFail + |> withErrorCode 224 + |> withDiagnosticMessageMatches "Option requires parameter" + |> ignore + + // ================================================================= + // Invalid integer argument — options expecting int get a non-integer + // ================================================================= + + [] + [] + [] + let ``int options with non-integer argument produce error 241`` (option: string) = + Fs """module M""" + |> withOptions [option] + |> compile + |> shouldFail + |> withErrorCode 241 + |> withDiagnosticMessageMatches "is not a valid integer argument" + |> ignore + + // ================================================================= + // Invalid warn level — out of valid range 0-5 + // ================================================================= + + [] + let ``warn with negative level produces error 1050`` () = + Fs """module M""" + |> withOptions ["--warn:-1"] + |> compile + |> shouldFail + |> withErrorCode 1050 + |> withDiagnosticMessageMatches "Invalid warning level" + |> ignore + + // ================================================================= + // Invalid target value + // ================================================================= + + [] + let ``target with unrecognized value produces error 1048`` () = + Fs """module M""" + |> withOptions ["--target:dll"] + |> compile + |> shouldFail + |> withErrorCode 1048 + |> withDiagnosticMessageMatches "Unrecognized target" + |> ignore + + // ================================================================= + // Invalid checksum algorithm + // ================================================================= + + [] + let ``checksumalgorithm with unsupported algorithm produces error 1065`` () = + Fs """module M""" + |> withOptions ["--checksumalgorithm:MD5"] + |> compile + |> shouldFail + |> withErrorCode 1065 + |> withDiagnosticMessageMatches "Algorithm.*is not supported" + |> ignore + + // ================================================================= + // Unrecognized option (error 243) + // ================================================================= + + [] + [] + [] // case-sensitive: --optimize (parsed as --optimize+) works, --Optimize does not + [] + let ``unrecognized options produce error 243`` (option: string) = + Fs """module M""" + |> withOptions [option] + |> compile + |> shouldFail + |> withErrorCode 243 + |> withDiagnosticMessageMatches "Unrecognized option" + |> ignore diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/UncoveredOptions.fs b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/UncoveredOptions.fs new file mode 100644 index 00000000000..dc0c6e0762f --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/CompilerOptions/Fsc/UncoveredOptions.fs @@ -0,0 +1,109 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace CompilerOptions.Fsc + +open Xunit +open FSharp.Test.Compiler + +/// Smoke tests for compiler options that have no dedicated test coverage +module UncoveredOptions = + + // ================================================================= + // Switch options (+/-) — verify parser accepts both polarities + // ================================================================= + + [] + [] + [] + [] + [] + [] + [] + [] + [] + [] + [] + [] + [] + [] + [] + let ``switch options are accepted by the parser`` (option: string) = + Fs """module M""" + |> withOptions [option] + |> ignoreWarnings + |> typecheck + |> shouldSucceed + |> ignore + + // ================================================================= + // Unit options (no argument) — verify parser accepts them + // ================================================================= + + [] + [] + [] + [] + [] + [] // typecheck only — compile would write .fsi files + [] + [] + let ``unit options are accepted by the parser`` (option: string) = + Fs """module M""" + |> withOptions [option] + |> ignoreWarnings + |> typecheck + |> shouldSucceed + |> ignore + + // ================================================================= + // String options with valid values — verify parser + compilation + // ================================================================= + + [] + [] + [] + let ``checksumalgorithm with valid algorithm is accepted`` (algorithm: string) = + Fs """module M""" + |> withOptions [$"--checksumalgorithm:{algorithm}"] + |> compile + |> shouldSucceed + |> ignore + + [] + [] + [] + [] + [] + let ``target with valid values is accepted`` (option: string) = + Fs """module M""" + |> withOptions [option] + |> ignoreWarnings + |> compile + |> shouldSucceed + |> ignore + + // ================================================================= + // Compilation modes + // ================================================================= + + [] + let ``parseonly does not report type errors`` () = + Fs """let x: int = "not an int" """ + |> asExe + |> withOptions ["--parseonly"] + |> ignoreWarnings + |> compile + |> shouldSucceed + |> ignore + + // ================================================================= + // Diagnostic format options + // ================================================================= + + [] + let ``maxerrors with valid value is accepted`` () = + Fs """module M""" + |> withOptions ["--maxerrors:10"] + |> compile + |> shouldSucceed + |> ignore diff --git a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/MemberConstraints.fs b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/MemberConstraints.fs index 9e04bc75dfc..d9b31531d15 100644 --- a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/MemberConstraints.fs +++ b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/MemberConstraints.fs @@ -114,6 +114,34 @@ ignore ["1" .. "42"] |> withSingleDiagnostic (Error 1, Line 2, Col 9, Line 2, Col 12, "The type 'string' does not support the operator 'op_Range'") + // https://github.com/dotnet/fsharp/issues/12386 + [] + let ``Issue 12386 - SRTP trait call should resolve correct overload at runtime`` () = + FSharp + """ +type A = + | A + static member ($) (A, _a: float) = 0.0 + static member ($) (A, _a: decimal) = 0M + static member ($) (A, _a: 't) = 0 + +let inline call x = ($) A x + +[] +let main _ = + let resultFloat = call 42.0 + let resultDecimal = call 42M + let resultInt = call 42 + if resultFloat <> 0.0 then failwithf "Expected 0.0 but got %A" resultFloat + if resultDecimal <> 0M then failwithf "Expected 0M but got %A" resultDecimal + if resultInt <> 0 then failwithf "Expected 0 but got %A" resultInt + printfn "All SRTP overload resolutions correct" + 0 + """ + |> asExe + |> compileExeAndRun + |> shouldSucceed + // https://github.com/dotnet/fsharp/issues/6648 [] let ``Issue 6648 - DU of DUs with inline static members should compile`` () = diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/CodeGenRegressions/CodeGenRegressions_Crashes.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/CodeGenRegressions/CodeGenRegressions_Crashes.fs new file mode 100644 index 00000000000..e49c3a3b211 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/CodeGenRegressions/CodeGenRegressions_Crashes.fs @@ -0,0 +1,428 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace EmittedIL + +open Xunit +open FSharp.Test +open FSharp.Test.Compiler +open FSharp.Test.Utilities + +module CodeGenRegressions_Crashes = + + let private getActualIL (result: CompilationResult) = + match result with + | CompilationResult.Success s -> + match s.OutputPath with + | Some p -> + let (_, _, actualIL) = ILChecker.verifyILAndReturnActual [] p [ "// dummy" ] + actualIL + | None -> failwith "No output path" + | _ -> failwith "Compilation failed" + + // https://github.com/dotnet/fsharp/issues/18956 + [] + let ``Issue_18956_DecimalConstantInvalidProgram`` () = + let source = """ +module A = + [] + let B = 42m + +[] +let main args = + printfn "%M" A.B + 0 +""" + FSharp source + |> asExe + |> withDebug + |> compile + |> shouldSucceed + |> run + |> shouldSucceed + |> ignore + + // https://github.com/dotnet/fsharp/issues/18319 + [] + let ``Issue_18319_LiteralUpcastMissingBox`` () = + let source = """ +module Test + +[] +let badobj: System.ValueType = 1 + +[] +let main _ = + System.Console.WriteLine(badobj) + 0 +""" + FSharp source + |> asExe + |> compile + |> shouldSucceed + |> run + |> shouldSucceed + |> ignore + + // https://github.com/dotnet/fsharp/issues/18319 + // Second case from issue: literal upcast used as default parameter value + [] + let ``Issue_18319_LiteralUpcastDefaultParam`` () = + let source = """ +module Test + +open System.Runtime.InteropServices + +[] +let lit: System.ValueType = 1 + +type C() = + static member M([] param: System.ValueType) = + System.Console.WriteLine(param) + +[] +let main _ = + C.M() + C.M(42) + 0 +""" + FSharp source + |> asExe + |> compile + |> shouldSucceed + |> run + |> shouldSucceed + |> ignore + + // https://github.com/dotnet/fsharp/issues/14508 + [] + let ``Issue_14508_NativeptrInInterfaces_CompileOnly`` () = + let source = """ +module Test + +open Microsoft.FSharp.NativeInterop + +type IFoo<'T when 'T : unmanaged> = + abstract member Pointer : nativeptr<'T> + +type Broken() = + member x.Pointer : nativeptr = Unchecked.defaultof<_> + interface IFoo with + member x.Pointer = x.Pointer + +type Working<'T when 'T : unmanaged>() = + member x.Pointer : nativeptr<'T> = Unchecked.defaultof<_> + interface IFoo<'T> with + member x.Pointer = x.Pointer +""" + FSharp source + |> asLibrary + |> compile + |> shouldSucceed + |> ignore + + // https://github.com/dotnet/fsharp/issues/14508 + [] + let ``Issue_14508_NativeptrInInterfaces_RuntimeWorking`` () = + let source = """ +open Microsoft.FSharp.NativeInterop + +type IFoo<'T when 'T : unmanaged> = + abstract member Pointer : nativeptr<'T> + +type Working<'T when 'T : unmanaged>() = + member x.Pointer : nativeptr<'T> = Unchecked.defaultof<_> + interface IFoo<'T> with + member x.Pointer = x.Pointer + +printfn "Working type loaded successfully" +""" + FSharp source + |> asExe + |> compile + |> shouldSucceed + |> run + |> shouldSucceed + |> ignore + + // https://github.com/dotnet/fsharp/issues/14508 + [] + let ``Issue_14508_NativeptrInInterfaces_RuntimeBroken`` () = + let source = """ +open Microsoft.FSharp.NativeInterop + +type IFoo<'T when 'T : unmanaged> = + abstract member Pointer : nativeptr<'T> + +type Broken() = + member x.Pointer : nativeptr = Unchecked.defaultof<_> + interface IFoo with + member x.Pointer = x.Pointer + +let b = Broken() +let p = (b :> IFoo).Pointer +printfn "Broken type loaded and Pointer accessed: %A" p +""" + FSharp source + |> asExe + |> compile + |> shouldSucceed + |> run + |> shouldSucceed + |> ignore + + // https://github.com/dotnet/fsharp/issues/14508 + [] + let ``Issue_14508_NativeptrInInterfaceParams`` () = + let source = """ +open Microsoft.FSharp.NativeInterop + +type IBar<'T when 'T : unmanaged> = + abstract member DoSomething : nativeptr<'T> -> unit + +type BarImpl() = + interface IBar with + member _.DoSomething(_p: nativeptr) = () + +let b = BarImpl() +(b :> IBar).DoSomething(Unchecked.defaultof<_>) +printfn "BarImpl loaded and DoSomething called" +""" + FSharp source + |> asExe + |> compile + |> shouldSucceed + |> run + |> shouldSucceed + |> ignore + + // https://github.com/dotnet/fsharp/issues/14508 + [] + let ``Issue_14508_NativeptrInInterfaceParamsAndReturn`` () = + let source = """ +open Microsoft.FSharp.NativeInterop + +type ITransform<'T when 'T : unmanaged> = + abstract member Transform : nativeptr<'T> -> nativeptr<'T> + +type TransformImpl() = + interface ITransform with + member _.Transform(p: nativeptr) : nativeptr = p + +let t = TransformImpl() +let result = (t :> ITransform).Transform(Unchecked.defaultof<_>) +printfn "TransformImpl loaded, result: %A" result +""" + FSharp source + |> asExe + |> compile + |> shouldSucceed + |> run + |> shouldSucceed + |> ignore + + // https://github.com/dotnet/fsharp/issues/14508 + [] + let ``Issue_14508_NativeptrInInterfaceParamWithOtherParams`` () = + let source = """ +open Microsoft.FSharp.NativeInterop + +type IProcess<'T when 'T : unmanaged> = + abstract member Process : nativeptr<'T> * int -> unit + +type ProcessImpl() = + interface IProcess with + member _.Process((_p: nativeptr), (_len: int)) = () + +let p = ProcessImpl() +(p :> IProcess).Process(Unchecked.defaultof<_>, 42) +printfn "ProcessImpl loaded and Process called" +""" + FSharp source + |> asExe + |> compile + |> shouldSucceed + |> run + |> shouldSucceed + |> ignore + + // https://github.com/dotnet/fsharp/issues/14508 + [] + let ``Issue_14508_NativeptrMultipleParams`` () = + let source = """ +open Microsoft.FSharp.NativeInterop + +type ICopy<'T when 'T : unmanaged> = + abstract member Copy : nativeptr<'T> * nativeptr<'T> * int -> unit + +type CopyImpl() = + interface ICopy with + member _.Copy((_src: nativeptr), (_dst: nativeptr), (_len: int)) = () + +let c = CopyImpl() +(c :> ICopy).Copy(Unchecked.defaultof<_>, Unchecked.defaultof<_>, 10) +printfn "CopyImpl loaded and Copy called" +""" + FSharp source + |> asExe + |> compile + |> shouldSucceed + |> run + |> shouldSucceed + |> ignore + + // https://github.com/dotnet/fsharp/issues/14492 + // Same-assembly variant: constrained generics inlined into closures trigger Specialize + [] + let ``Issue_14492_SameAssemblyInline`` () = + let source = """ +module Test + +#nowarn "3370" + +let inline refEquals<'a when 'a : not struct> (a : 'a) (b : 'a) = obj.ReferenceEquals (a, b) + +let inline tee f x = + f x + x + +let memoizeLatestRef (f: 'a -> 'b) = + let cell = ref None + let f' (x: 'a) = + match !cell with + | Some (x', value) when refEquals x' x -> value + | _ -> f x |> tee (fun y -> cell := Some (x, y)) + f' + +module BugInReleaseConfig = + let test f x = + printfn "%s" (f x) + + let f: string -> string = memoizeLatestRef id + + let run () = test f "ok" + +[] +let main _ = + BugInReleaseConfig.run () + 0 +""" + FSharp source + |> asExe + |> withOptimize + |> compile + |> shouldSucceed + |> run + |> shouldSucceed + |> ignore + + // https://github.com/dotnet/fsharp/issues/13447 + [] + let ``Issue_13447_TailInstructionCorruption`` () = + let source = """ +module Test +open System +open Microsoft.FSharp.NativeInterop + +#nowarn "9" // Uses of this construct may result in the generation of unverifiable .NET IL code + +[] +type MyResult<'T, 'E> = + | Ok of value: 'T + | Error of error: 'E + +let useStackAlloc () : MyResult = + let ptr = NativePtr.stackalloc 100 + let span = Span(NativePtr.toVoidPtr ptr, 100) + span.[0] <- 42uy + Ok (int span.[0]) + +let test () = + match useStackAlloc () with + | Ok v -> v + | Error _ -> -1 +""" + let actualIL = + FSharp source + |> asLibrary + |> withOptimize + |> compile + |> shouldSucceed + |> getActualIL + + let useStackAllocIdx = actualIL.IndexOf("useStackAlloc") + Assert.True(useStackAllocIdx >= 0, "useStackAlloc method not found in IL") + let methodEnd = actualIL.IndexOf("\n } ", useStackAllocIdx) + let methodIL = if methodEnd > 0 then actualIL.Substring(useStackAllocIdx, methodEnd - useStackAllocIdx) else actualIL.Substring(useStackAllocIdx) + Assert.DoesNotContain("tail.", methodIL) + + // https://github.com/dotnet/fsharp/issues/11132 + [] + let ``Issue_11132_VoidptrDelegate`` () = + let source = """ +module Test +#nowarn "9" + +open System + +type MyDelegate = delegate of voidptr -> unit + +let method (ptr: voidptr) = () + +let getDelegate (m: voidptr -> unit) : MyDelegate = MyDelegate(m) + +let test() = + let d = getDelegate method + d.Invoke(IntPtr.Zero.ToPointer()) + +do test() +""" + FSharp source + |> asExe + |> withOptimize + |> compileAndRun + |> shouldSucceed + |> ignore + + // https://github.com/dotnet/fsharp/issues/14492 + // Variant: cross-assembly inlining with constrained generics triggers Specialize closure + [] + let ``Issue_14492_CrossAssemblyInline`` () = + let lib = FSharp """ +module Lib + +let inline refEquals<'a when 'a : not struct> (a : 'a) (b : 'a) = obj.ReferenceEquals (a, b) + +let inline tee f x = + f x + x +""" |> withOptimize + |> asLibrary + + let app = FSharp """ +module App + +open Lib + +let memoizeLatestRef (f: 'a -> 'b) = + let cell = ref None + let f' (x: 'a) = + match cell.Value with + | Some (x', value) when refEquals x' x -> value + | _ -> f x |> tee (fun y -> cell.Value <- Some (x, y)) + f' + +module Test = + let f: string -> string = memoizeLatestRef id + let run () = printfn "%s" (f "ok") + +[] +let main _ = Test.run(); 0 +""" + app + |> withOptimize + |> asExe + |> withReferences [lib] + |> compileAndRun + |> shouldSucceed + |> ignore + diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/StaticOptimizations/String_Enum.fs.il.bsl b/tests/FSharp.Compiler.ComponentTests/EmittedIL/StaticOptimizations/String_Enum.fs.il.bsl index 740505ce6a7..1af36573396 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/StaticOptimizations/String_Enum.fs.il.bsl +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/StaticOptimizations/String_Enum.fs.il.bsl @@ -540,4 +540,3 @@ - diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TestFunctions/TestFunction03.fs.OptimizeOff.il.bsl b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TestFunctions/TestFunction03.fs.OptimizeOff.il.bsl index dd6e6ab1b40..9de983bbfa2 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TestFunctions/TestFunction03.fs.OptimizeOff.il.bsl +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TestFunctions/TestFunction03.fs.OptimizeOff.il.bsl @@ -16,16 +16,6 @@ .hash algorithm 0x00008004 .ver 0:0:0:0 -} -.mresource public FSharpSignatureCompressedData.assembly -{ - - -} -.mresource public FSharpOptimizationCompressedData.assembly -{ - - } .module assembly.exe @@ -112,4 +102,3 @@ - diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TestFunctions/TestFunction03.fs.OptimizeOn.il.release.bsl b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TestFunctions/TestFunction03.fs.OptimizeOn.il.release.bsl index d360b2a91eb..aa576cc3d13 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TestFunctions/TestFunction03.fs.OptimizeOn.il.release.bsl +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TestFunctions/TestFunction03.fs.OptimizeOn.il.release.bsl @@ -123,4 +123,3 @@ - diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TestFunctions/TestFunction03b.fs.OptimizeOff.il.bsl b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TestFunctions/TestFunction03b.fs.OptimizeOff.il.bsl index 6b0f2cd2642..15905929f58 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TestFunctions/TestFunction03b.fs.OptimizeOff.il.bsl +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TestFunctions/TestFunction03b.fs.OptimizeOff.il.bsl @@ -16,16 +16,6 @@ .hash algorithm 0x00008004 .ver 0:0:0:0 -} -.mresource public FSharpSignatureCompressedData.assembly -{ - - -} -.mresource public FSharpOptimizationCompressedData.assembly -{ - - } .module assembly.exe @@ -126,4 +116,3 @@ - diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TestFunctions/TestFunction03b.fs.OptimizeOn.il.release.bsl b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TestFunctions/TestFunction03b.fs.OptimizeOn.il.release.bsl index 0a3ae50e3e0..05caaf30094 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TestFunctions/TestFunction03b.fs.OptimizeOn.il.release.bsl +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TestFunctions/TestFunction03b.fs.OptimizeOn.il.release.bsl @@ -130,4 +130,3 @@ - diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TestFunctions/TestFunction03c.fs.OptimizeOff.il.bsl b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TestFunctions/TestFunction03c.fs.OptimizeOff.il.bsl index 0eeedf099d9..f67fcf38d92 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TestFunctions/TestFunction03c.fs.OptimizeOff.il.bsl +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TestFunctions/TestFunction03c.fs.OptimizeOff.il.bsl @@ -8,7 +8,7 @@ .assembly extern netstandard { .publickeytoken = (CC 7B 13 FF CD 2D DD 51 ) - .ver 2:0:0:0 + .ver 2:1:0:0 } .assembly assembly { @@ -21,16 +21,6 @@ .hash algorithm 0x00008004 .ver 0:0:0:0 -} -.mresource public FSharpSignatureCompressedData.assembly -{ - - -} -.mresource public FSharpOptimizationCompressedData.assembly -{ - - } .module assembly.exe @@ -145,4 +135,3 @@ - diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TestFunctions/TestFunction03c.fs.OptimizeOn.il.release.bsl b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TestFunctions/TestFunction03c.fs.OptimizeOn.il.release.bsl index ad3903afb89..3dbb67a8837 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TestFunctions/TestFunction03c.fs.OptimizeOn.il.release.bsl +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TestFunctions/TestFunction03c.fs.OptimizeOn.il.release.bsl @@ -141,4 +141,3 @@ - diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs index b5cf1bcbccb..bc68eb4d7d9 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs @@ -277,3 +277,19 @@ let main _argv = """ |> typecheck |> shouldSucceed + + // https://github.com/dotnet/fsharp/issues/4473 + [] + let ``Issue 4473 - extern function parameters not flagged as unused with warnon 1182``() = + FSharp """ +module Test4473 + +open System.Runtime.InteropServices + +[] +extern bool Beep(int frequency, int duration) + """ + |> withWarnOn 1182 + |> asLibrary + |> typecheck + |> shouldSucceed diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index f5d1048408e..a345c31d728 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -283,6 +283,7 @@ + @@ -417,6 +418,8 @@ + + diff --git a/tests/FSharp.Compiler.ComponentTests/Language/AttributeCheckingTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/AttributeCheckingTests.fs index d91997b68f0..898706bca5c 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/AttributeCheckingTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/AttributeCheckingTests.fs @@ -77,3 +77,114 @@ type C() = |> withReferences [csharpBaseClass] |> compile |> shouldSucceed + + [] + let ``C# attribute subclass inherits AllowMultiple true from base`` () = + let csharpLib = + CSharp """ + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BaseAttribute : Attribute { } + public class ChildAttribute : BaseAttribute { } + """ |> withName "csAttrLib" + + FSharp """ +module Test + +[] +type C() = class end + """ + |> withReferences [csharpLib] + |> compile + |> shouldSucceed + + [] + let ``C# attribute subclass inherits AllowMultiple false from base`` () = + let csharpLib = + CSharp """ + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = false)] + public class BaseAttribute : Attribute { } + public class ChildAttribute : BaseAttribute { } + """ |> withName "csAttrLib" + + FSharp """ +module Test + +[] +type C() = class end + """ + |> withReferences [csharpLib] + |> compile + |> shouldFail + |> withSingleDiagnostic (Error 429, Line 4, Col 10, Line 4, Col 15, "The attribute type 'ChildAttribute' has 'AllowMultiple=false'. Multiple instances of this attribute cannot be attached to a single language element.") + + [] + let ``C# attribute multi-level inheritance inherits AllowMultiple true`` () = + let csharpLib = + CSharp """ + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BaseAttribute : Attribute { } + public class MiddleAttribute : BaseAttribute { } + public class LeafAttribute : MiddleAttribute { } + """ |> withName "csAttrLib" + + FSharp """ +module Test + +[] +type C() = class end + """ + |> withReferences [csharpLib] + |> compile + |> shouldSucceed + + [] + let ``C# attribute subclass with own AttributeUsage overrides base AllowMultiple`` () = + let csharpLib = + CSharp """ + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BaseAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = false)] + public class ChildAttribute : BaseAttribute { } + """ |> withName "csAttrLib" + + FSharp """ +module Test + +[] +type C() = class end + """ + |> withReferences [csharpLib] + |> compile + |> shouldFail + |> withSingleDiagnostic (Error 429, Line 4, Col 10, Line 4, Col 15, "The attribute type 'ChildAttribute' has 'AllowMultiple=false'. Multiple instances of this attribute cannot be attached to a single language element.") + + [] + let ``F# attribute subclass of C# base inherits AllowMultiple true`` () = + let csharpLib = + CSharp """ + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BaseAttribute : Attribute { } + """ |> withName "csAttrLib" + + FSharp """ +module Test + +type ChildAttribute() = inherit BaseAttribute() + +[] +type C() = class end + """ + |> withReferences [csharpLib] + |> compile + |> shouldSucceed diff --git a/tests/FSharp.Compiler.ComponentTests/Language/ComputationExpressionTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/ComputationExpressionTests.fs index 13652e42ef3..9285e588f46 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/ComputationExpressionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/ComputationExpressionTests.fs @@ -2270,6 +2270,9 @@ let data2 = [(1, "one"); (2, "two"); (3, "three")] let result = query { for x in data1 do join (y, name) in data2 on (x = y); select name }""" "let result = query { for a in [1;2;3] do for b in [4;5;6] do where (a < b); select (a + b) }" "let result = query { for x in [3;1;2] do sortBy x; select x }" + // https://github.com/dotnet/fsharp/issues/14566 + """let result = query { join a in ["x"] on ("x" = a); join b in ["y"] on ("y" = b); select a }""" + """let result = query { for r in [1;2;3] do for i in [true; false] do where i; select r }""" ] |> List.map (fun s -> [| box s |]) diff --git a/tests/FSharp.Compiler.ComponentTests/Scripting/Interactive.fs b/tests/FSharp.Compiler.ComponentTests/Scripting/Interactive.fs index ea7e3fbe312..0d597f182e2 100644 --- a/tests/FSharp.Compiler.ComponentTests/Scripting/Interactive.fs +++ b/tests/FSharp.Compiler.ComponentTests/Scripting/Interactive.fs @@ -153,6 +153,76 @@ module MultiEmit = """if B.v <> 9.3 then failwith $"9: Failed {A.v} <> 9.3" """ |] |> Seq.iter(fun item -> item |> scriptIt) + // https://github.com/dotnet/fsharp/issues/12386 + // The bug manifests when the SRTP-constrained inline function is defined in one FSI submission + // and called in a separate submission. Single-unit compilation works fine. + // When a type has 2+ specific overloads plus a generic catch-all for operator ($), the SRTP + // constraint stays unresolved across submissions, causing value restriction (FS0030) or wrong + // runtime dispatch (NullReferenceException). Works fine within a single submission and in + // multi-file compiled projects. + [] + [] + [] + let ``Issue 12386 - SRTP trait call resolves correct overload across FSI submissions`` (useMultiEmit) = + let args: string array = [| if useMultiEmit then "--multiemit+" else "--multiemit-" |] + use session = new FSharpScript(additionalArgs = args) + + // Submission 1: Define type with overloaded ($) and an inline function using SRTP + session.Eval( + """ +type A = A with + static member ($) (A, a: float ) = 0.0 + static member ($) (A, a: decimal) = 0M + static member ($) (A, a: 't ) = 0 + +let inline call x = ($) A x +""" + ) + |> ignoreValue + + // Submission 2: Call with float - should resolve to the float overload, not the generic one + let result = session.Eval("call 42.") |> getValue + let fsiVal = result.Value + Assert.Equal(typeof, fsiVal.ReflectionType) + Assert.Equal(0.0, fsiVal.ReflectionValue :?> float) + + // Submission 3: Call with decimal - should resolve to the decimal overload + let result2 = session.Eval("call 42M") |> getValue + let fsiVal2 = result2.Value + Assert.Equal(typeof, fsiVal2.ReflectionType) + Assert.Equal(0M, fsiVal2.ReflectionValue :?> decimal) + + // Same scenario as Issue 12386 but via compiled cross-project reference (not FSI). + // This verifies whether the bug is FSI-specific or also affects project references. + [] + let ``Issue 12386 - SRTP trait call resolves correct overload across project references`` () = + let lib = + FSharp + """ +namespace Lib +type A = A with + static member ($) (A, a: float ) = 0.0 + static member ($) (A, a: decimal) = 0M + static member ($) (A, a: 't ) = 0 + +module Calls = + let inline call x = ($) A x +""" + |> asLibrary + + FSharp + """ +module App +open Lib.Calls + +let result = call 42. +if result <> 0.0 then failwithf "Expected 0.0 but got %A" result +""" + |> withReferences [ lib ] + |> asExe + |> compileExeAndRun + |> shouldSucceed + [] let ``Version directive displays version and environment info``() = Fsx """ diff --git a/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs b/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs index cd494f7e659..551bdf02ea2 100644 --- a/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs @@ -3512,12 +3512,6 @@ let ``Test Project24 all symbols`` () = ("v", "file1", ((22, 17), (22, 18)), ["defn"], []); ("int", "file1", ((25, 21), (25, 24)), ["type"], ["abbrev"]); ("v", "file1", ((25, 18), (25, 19)), ["defn"], []); - ("``AutoPropGet@``", "file1", ((27, 15), (27, 26)), [], ["compgen"]); - ("``AutoPropGetSet@``", "file1", ((28, 15), (28, 29)), [], ["compgen"; "mutable"]) - ("v", "file1", ((28, 15), (28, 29)), ["defn"], []); - ("``StaticAutoPropGet@``", "file1", ((30, 22), (30, 39)), [], ["compgen"]); - ("``StaticAutoPropGetSet@``", "file1", ((31, 22), (31, 42)), [], - ["compgen"; "mutable"]); ("v", "file1", ((31, 22), (31, 42)), ["defn"], []); ("``.cctor``", "file1", ((4, 5), (4, 23)), ["defn"], ["member"]); ("TypeWithProperties", "file1", ((33, 9), (33, 27)), [], ["member"; "ctor"]); ("NameGetSet", "file1", ((33, 9), (33, 40)), [], ["member"; "prop"]); @@ -3614,12 +3608,6 @@ let ``Test symbol uses of properties with both getters and setters`` () = ("v", "file1", ((22, 17), (22, 18)), []); ("int", "file1", ((25, 21), (25, 24)), ["abbrev"]); ("v", "file1", ((25, 18), (25, 19)), []); - ("``AutoPropGet@``", "file1", ((27, 15), (27, 26)), ["compgen"]); - ("``AutoPropGetSet@``", "file1", ((28, 15), (28, 29)), ["compgen"; "mutable"]); - ("v", "file1", ((28, 15), (28, 29)), []); - ("``StaticAutoPropGet@``", "file1", ((30, 22), (30, 39)), ["compgen"]); - ("``StaticAutoPropGetSet@``", "file1", ((31, 22), (31, 42)), - ["compgen"; "mutable"]); ("v", "file1", ((31, 22), (31, 42)), []); ("``.cctor``", "file1", ((4, 5), (4, 23)), ["member"]); ("TypeWithProperties", "file1", ((33, 9), (33, 27)), ["member"; "ctor"]); ("NameGetSet", "file1", ((33, 9), (33, 40)), ["member"; "prop"]); diff --git a/tests/FSharp.Compiler.Service.Tests/Symbols.fs b/tests/FSharp.Compiler.Service.Tests/Symbols.fs index 3f4d4fcdbd9..69bfb70519a 100644 --- a/tests/FSharp.Compiler.Service.Tests/Symbols.fs +++ b/tests/FSharp.Compiler.Service.Tests/Symbols.fs @@ -634,18 +634,23 @@ type Foo = Assert.True(setMfv.CompiledName.StartsWith("set_")) | _ -> failwith $"Expected three symbols, got %A{symbols}" - [] - let ``AutoProperty with get, set has property symbol 02`` () = - let symbol = Checker.getSymbolUse """ + // https://github.com/dotnet/fsharp/issues/3939 + [] + let ``AutoProperty with get, set does not expose compiler-generated v symbol`` () = + let _, checkResults = getParseAndCheckResults """ namespace Foo type Foo = - member val AutoPropGetSet{caret} = 0 with get, set + member val AutoPropGetSet = 0 with get, set """ - // The setter should have a symbol for the generated parameter `v`. - let setVMfv = symbol |> chooseMemberOrFunctionOrValue - if Option.isNone setVMfv then - failwith "No generated v symbol for the setter was found" + let allSymbols = checkResults.GetAllUsesOfAllSymbolsInFile() + let allMfvs = allSymbols |> Seq.choose (fun su -> match su.Symbol with :? FSharpMemberOrFunctionOrValue as mfv -> Some mfv | _ -> None) |> Seq.toList + // The compiler-generated `v` setter parameter should NOT appear in symbol uses + let vSymbols = allMfvs |> List.filter (fun mfv -> mfv.DisplayName = "v") + Assert.True(vSymbols.IsEmpty, $"Compiler-generated 'v' symbol should not be exposed via GetAllUsesOfAllSymbolsInFile, but found {vSymbols.Length} occurrences") + // The compiler-generated backing field should also not appear + let backingFieldSymbols = allMfvs |> List.filter (fun mfv -> mfv.DisplayName.Contains("@")) + Assert.True(backingFieldSymbols.IsEmpty, $"Compiler-generated backing field should not appear, but found: {backingFieldSymbols |> List.map (fun m -> m.DisplayName)}") [] let ``Property symbol is resolved for property`` () = diff --git a/tests/FSharp.Compiler.Service.Tests/TreeVisitorTests.fs b/tests/FSharp.Compiler.Service.Tests/TreeVisitorTests.fs index 2895a53210c..7d8b10502d3 100644 --- a/tests/FSharp.Compiler.Service.Tests/TreeVisitorTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/TreeVisitorTests.fs @@ -167,3 +167,111 @@ type Meh = match SyntaxTraversal.Traverse(pos, parseTree, visitor) with | Some "Foo" -> () | _ -> failwith "Did not visit SynValSig in SynMemberSig.Member" + +// https://github.com/dotnet/fsharp/issues/13114 +[] +let ``Issue 13114 - defaultTraverse walks into SynPat.Record`` () = + let visitor = + { new SyntaxVisitorBase<_>() with + member x.VisitExpr(_, _, defaultTraverse, expr) = defaultTraverse expr + + member x.VisitPat(_, defaultTraverse, pat) = + match pat with + | SynPat.Named _ -> Some pat + | _ -> defaultTraverse pat } + + let source = + """ +type R = { A: int } +let f (r: R) = + match r with + | { A = a } -> a +""" + + let parseTree = parseSourceCode ("C:\\test.fs", source) + + match SyntaxTraversal.Traverse(mkPos 5 13, parseTree, visitor) with + | Some(SynPat.Named(ident = SynIdent(ident = ident))) when ident.idText = "a" -> () + | other -> failwith $"defaultTraverse did not walk into SynPat.Record fields, got: %A{other}" + +// https://github.com/dotnet/fsharp/issues/13114 +[] +let ``Issue 13114 - defaultTraverse walks into SynPat.QuoteExpr`` () = + let visitor = + { new SyntaxVisitorBase<_>() with + member x.VisitExpr(_, _, defaultTraverse, expr) = + match expr with + | SynExpr.Const(SynConst.Int32 42, _) -> Some expr + | _ -> defaultTraverse expr + + member x.VisitPat(_, defaultTraverse, pat) = defaultTraverse pat } + + let source = + """ +let f x = + match x with + | <@ 42 @> -> () + | _ -> () +""" + + let parseTree = parseSourceCode ("C:\\test.fs", source) + + match SyntaxTraversal.Traverse(mkPos 4 8, parseTree, visitor) with + | Some(SynExpr.Const(SynConst.Int32 42, _)) -> () + | other -> failwith $"defaultTraverse did not walk into SynPat.QuoteExpr, got: %A{other}" + +// https://github.com/dotnet/fsharp/issues/13114 +[] +let ``Issue 13114 - defaultTraverse walks into SynPat.FromParseError`` () = + let visitor = + { new SyntaxVisitorBase<_>() with + member x.VisitExpr(_, _, defaultTraverse, expr) = defaultTraverse expr + + member x.VisitPat(_, defaultTraverse, pat) = + match pat with + | SynPat.Named _ -> Some pat + | _ -> defaultTraverse pat } + + // SynPat.FromParseError wraps the inner pat when a parse error occurs; + // defaultTraverse should descend into the wrapped pattern. + let source = + """ +let f x = + match x with + | (a) -> a +""" + + let parseTree = parseSourceCode ("C:\\test.fs", source) + + match SyntaxTraversal.Traverse(mkPos 4 7, parseTree, visitor) with + | Some(SynPat.Named _) -> () + | other -> failwith $"defaultTraverse did not walk into nested SynPat, got: %A{other}" + +// https://github.com/dotnet/fsharp/issues/13114 +[] +let ``Issue 13114 - defaultTraverse walks into SynPat.IsInst`` () = + let visitor = + { new SyntaxVisitorBase<_>() with + member x.VisitExpr(_, _, defaultTraverse, expr) = defaultTraverse expr + + member x.VisitPat(_, defaultTraverse, pat) = defaultTraverse pat + + member x.VisitType(_, _, ty) = + match ty with + | SynType.LongIdent _ -> Some ty + | _ -> None } + + let source = + """ +let f (x: obj) = + match x with + | :? string -> () + | _ -> () +""" + + let parseTree = parseSourceCode ("C:\\test.fs", source) + + match SyntaxTraversal.Traverse(mkPos 4 11, parseTree, visitor) with + | Some(SynType.LongIdent _) -> () + | other -> failwith $"defaultTraverse did not walk into SynPat.IsInst, got: %A{other}" + diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs index 3bf50a19fb3..d1d09dcd8a1 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs @@ -255,10 +255,13 @@ type AsyncType() = [] member _.CancellationPropagatesToTask () = + let ewh = new ManualResetEvent(false) let a = async { + ewh.Set() |> Assert.True while true do () } let t = Async.StartAsTask a + ewh.WaitOne() |> Assert.True Async.CancelDefaultToken () let mutable exceptionThrown = false try