Skip to content

Commit fafd381

Browse files
committed
Merge tag 'nightly-2025-04-02' into releases/v4.19.0
2 parents 3cb82da + 1ee7e1a commit fafd381

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1126
-211
lines changed

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ foreach(var ${vars})
2020
elseif("${var}" MATCHES "LLVM*|PKG_CONFIG|USE_LAKE|USE_MIMALLOC")
2121
list(APPEND STAGE0_ARGS "-D${var}=${${var}}")
2222
endif()
23+
elseif("${var}" MATCHES "USE_MIMALLOC")
24+
list(APPEND CL_ARGS "-D${var}=${${var}}")
25+
list(APPEND STAGE0_ARGS "-D${var}=${${var}}")
2326
elseif(("${var}" MATCHES "CMAKE_.*") AND NOT ("${var}" MATCHES "CMAKE_BUILD_TYPE") AND NOT ("${var}" MATCHES "CMAKE_HOME_DIRECTORY"))
2427
list(APPEND PLATFORM_ARGS "-D${var}=${${var}}")
2528
endif()

CMakePresets.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"LEANC_EXTRA_CC_FLAGS": "-fsanitize=address,undefined",
3131
"LEAN_EXTRA_LINKER_FLAGS": "-fsanitize=address,undefined -fsanitize-link-c++-runtime",
3232
"SMALL_ALLOCATOR": "OFF",
33+
"USE_MIMALLOC": "OFF",
3334
"BSYMBOLIC": "OFF",
3435
"LEAN_TEST_VARS": "MAIN_STACK_SIZE=16000"
3536
},

script/merge_remote.py

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
#!/usr/bin/env python3
2+
3+
"""
4+
Merge a tag into a branch on a GitHub repository.
5+
6+
This script checks if a specified tag can be merged cleanly into a branch and performs
7+
the merge if possible. If the merge cannot be done cleanly, it prints a helpful message.
8+
9+
Usage:
10+
python3 merge_remote.py <org/repo> <branch> <tag>
11+
12+
Arguments:
13+
org/repo: GitHub repository in the format 'organization/repository'
14+
branch: The target branch to merge into
15+
tag: The tag to merge from
16+
17+
Example:
18+
python3 merge_remote.py leanprover/mathlib4 stable v4.6.0
19+
20+
The script uses the GitHub CLI (`gh`), so make sure it's installed and authenticated.
21+
"""
22+
23+
import argparse
24+
import subprocess
25+
import sys
26+
import tempfile
27+
import os
28+
import shutil
29+
30+
31+
def run_command(command, check=True, capture_output=True):
32+
"""Run a shell command and return the result."""
33+
try:
34+
result = subprocess.run(
35+
command,
36+
check=check,
37+
shell=True,
38+
text=True,
39+
capture_output=capture_output
40+
)
41+
return result
42+
except subprocess.CalledProcessError as e:
43+
if capture_output:
44+
print(f"Command failed: {command}")
45+
print(f"Error: {e.stderr}")
46+
return e
47+
48+
49+
def clone_repo(repo, temp_dir):
50+
"""Clone the repository to a temporary directory using shallow clone."""
51+
print(f"Shallow cloning {repo}...")
52+
# Use --depth=1 for shallow clone
53+
clone_result = run_command(f"gh repo clone {repo} {temp_dir} -- --depth=1", check=False)
54+
if clone_result.returncode != 0:
55+
print(f"Failed to clone repository {repo}.")
56+
print(f"Error: {clone_result.stderr}")
57+
return False
58+
return True
59+
60+
61+
def check_and_merge(repo, branch, tag, temp_dir):
62+
"""Check if tag can be merged into branch and perform the merge if possible."""
63+
# Change to the temporary directory
64+
os.chdir(temp_dir)
65+
66+
# Fetch only the specific branch and tag we need
67+
print(f"Fetching branch '{branch}' and tag '{tag}'...")
68+
fetch_branch = run_command(f"git fetch origin {branch}")
69+
if fetch_branch.returncode != 0:
70+
print(f"Error: Failed to fetch branch '{branch}'.")
71+
return False
72+
73+
fetch_tag = run_command(f"git fetch origin tag {tag}")
74+
if fetch_tag.returncode != 0:
75+
print(f"Error: Failed to fetch tag '{tag}'.")
76+
return False
77+
78+
# Check if branch exists
79+
branch_check = run_command(f"git branch -r | grep origin/{branch}")
80+
if branch_check.returncode != 0:
81+
print(f"Error: Branch '{branch}' does not exist in repository.")
82+
return False
83+
84+
# Check if tag exists
85+
tag_check = run_command(f"git tag -l {tag}")
86+
if tag_check.returncode != 0 or not tag_check.stdout.strip():
87+
print(f"Error: Tag '{tag}' does not exist in repository.")
88+
return False
89+
90+
# Checkout the branch
91+
print(f"Checking out branch '{branch}'...")
92+
checkout_result = run_command(f"git checkout -b {branch} origin/{branch}")
93+
if checkout_result.returncode != 0:
94+
return False
95+
96+
# Try merging the tag in a dry-run to check if it can be merged cleanly
97+
print(f"Checking if {tag} can be merged cleanly into {branch}...")
98+
merge_check = run_command(f"git merge --no-commit --no-ff {tag}", check=False)
99+
100+
if merge_check.returncode != 0:
101+
print(f"Cannot merge {tag} cleanly into {branch}.")
102+
print("Merge conflicts would occur. Aborting merge.")
103+
run_command("git merge --abort")
104+
return False
105+
106+
# Abort the test merge
107+
run_command("git reset --hard HEAD")
108+
109+
# Now perform the actual merge and push to remote
110+
print(f"Merging {tag} into {branch}...")
111+
merge_result = run_command(f"git merge {tag} --no-edit")
112+
if merge_result.returncode != 0:
113+
print(f"Failed to merge {tag} into {branch}.")
114+
return False
115+
116+
print(f"Pushing changes to remote...")
117+
push_result = run_command(f"git push origin {branch}")
118+
if push_result.returncode != 0:
119+
print(f"Failed to push changes to remote.")
120+
return False
121+
122+
print(f"Successfully merged {tag} into {branch} and pushed to remote.")
123+
return True
124+
125+
126+
def main():
127+
parser = argparse.ArgumentParser(
128+
description="Merge a tag into a branch on a GitHub repository.",
129+
formatter_class=argparse.RawDescriptionHelpFormatter,
130+
epilog="""
131+
Examples:
132+
%(prog)s leanprover/mathlib4 stable v4.6.0 Merge tag v4.6.0 into stable branch
133+
134+
The script will:
135+
1. Clone the repository
136+
2. Check if the tag and branch exist
137+
3. Check if the tag can be merged cleanly into the branch
138+
4. Perform the merge and push to remote if possible
139+
"""
140+
)
141+
parser.add_argument("repo", help="GitHub repository in the format 'organization/repository'")
142+
parser.add_argument("branch", help="The target branch to merge into")
143+
parser.add_argument("tag", help="The tag to merge from")
144+
145+
args = parser.parse_args()
146+
147+
# Create a temporary directory for the repository
148+
temp_dir = tempfile.mkdtemp()
149+
try:
150+
# Clone the repository
151+
if not clone_repo(args.repo, temp_dir):
152+
sys.exit(1)
153+
154+
# Check if the tag can be merged and perform the merge
155+
if not check_and_merge(args.repo, args.branch, args.tag, temp_dir):
156+
sys.exit(1)
157+
158+
finally:
159+
# Clean up the temporary directory
160+
print(f"Cleaning up temporary files...")
161+
shutil.rmtree(temp_dir)
162+
163+
164+
if __name__ == "__main__":
165+
main()

script/release_checklist.py

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@
88
import sys
99
import os
1010

11+
def debug(verbose, message):
12+
"""Print debug message if verbose mode is enabled."""
13+
if verbose:
14+
print(f" [DEBUG] {message}")
15+
1116
def parse_repos_config(file_path):
1217
with open(file_path, "r") as f:
1318
return yaml.safe_load(f)["repositories"]
@@ -90,14 +95,15 @@ def is_version_gte(version1, version2):
9095
return False
9196
return parse_version(version1) >= parse_version(version2)
9297

93-
def is_merged_into_stable(repo_url, tag_name, stable_branch, github_token):
98+
def is_merged_into_stable(repo_url, tag_name, stable_branch, github_token, verbose=False):
9499
# First get the commit SHA for the tag
95100
api_base = repo_url.replace("https://github.com/", "https://api.github.com/repos/")
96101
headers = {'Authorization': f'token {github_token}'} if github_token else {}
97102

98103
# Get tag's commit SHA
99104
tag_response = requests.get(f"{api_base}/git/refs/tags/{tag_name}", headers=headers)
100105
if tag_response.status_code != 200:
106+
debug(verbose, f"Could not fetch tag {tag_name}, status code: {tag_response.status_code}")
101107
return False
102108

103109
# Handle both single object and array responses
@@ -106,22 +112,48 @@ def is_merged_into_stable(repo_url, tag_name, stable_branch, github_token):
106112
# Find the exact matching tag in the list
107113
matching_tags = [tag for tag in tag_data if tag['ref'] == f'refs/tags/{tag_name}']
108114
if not matching_tags:
115+
debug(verbose, f"No matching tag found for {tag_name} in response list")
109116
return False
110117
tag_sha = matching_tags[0]['object']['sha']
111118
else:
112119
tag_sha = tag_data['object']['sha']
113-
120+
121+
# Check if the tag is an annotated tag and get the actual commit SHA
122+
if tag_data.get('object', {}).get('type') == 'tag' or (
123+
isinstance(tag_data, list) and
124+
matching_tags and
125+
matching_tags[0].get('object', {}).get('type') == 'tag'):
126+
127+
# Get the commit that this tag points to
128+
tag_obj_response = requests.get(f"{api_base}/git/tags/{tag_sha}", headers=headers)
129+
if tag_obj_response.status_code == 200:
130+
tag_obj = tag_obj_response.json()
131+
if 'object' in tag_obj and tag_obj['object']['type'] == 'commit':
132+
commit_sha = tag_obj['object']['sha']
133+
debug(verbose, f"Tag is annotated. Resolved commit SHA: {commit_sha}")
134+
tag_sha = commit_sha # Use the actual commit SHA
135+
114136
# Get commits on stable branch containing this SHA
115137
commits_response = requests.get(
116138
f"{api_base}/commits?sha={stable_branch}&per_page=100",
117139
headers=headers
118140
)
119141
if commits_response.status_code != 200:
142+
debug(verbose, f"Could not fetch commits for branch {stable_branch}, status code: {commits_response.status_code}")
120143
return False
121144

122145
# Check if any commit in stable's history matches our tag's SHA
123146
stable_commits = [commit['sha'] for commit in commits_response.json()]
124-
return tag_sha in stable_commits
147+
148+
is_merged = tag_sha in stable_commits
149+
150+
debug(verbose, f"Tag SHA: {tag_sha}")
151+
debug(verbose, f"First 5 stable commits: {stable_commits[:5]}")
152+
debug(verbose, f"Total stable commits fetched: {len(stable_commits)}")
153+
if not is_merged:
154+
debug(verbose, f"Tag SHA not found in first {len(stable_commits)} commits of stable branch")
155+
156+
return is_merged
125157

126158
def is_release_candidate(version):
127159
return "-rc" in version
@@ -195,13 +227,15 @@ def pr_exists_with_title(repo_url, title, github_token):
195227
return None
196228

197229
def main():
198-
github_token = get_github_token()
230+
parser = argparse.ArgumentParser(description="Check release status of Lean4 repositories")
231+
parser.add_argument("toolchain", help="The toolchain version to check (e.g., v4.6.0)")
232+
parser.add_argument("--verbose", "-v", action="store_true", help="Enable verbose debugging output")
233+
args = parser.parse_args()
199234

200-
if len(sys.argv) != 2:
201-
print("Usage: python3 release_checklist.py <toolchain>")
202-
sys.exit(1)
203-
204-
toolchain = sys.argv[1]
235+
github_token = get_github_token()
236+
toolchain = args.toolchain
237+
verbose = args.verbose
238+
205239
stripped_toolchain = strip_rc_suffix(toolchain)
206240
lean_repo_url = "https://github.com/leanprover/lean4"
207241

@@ -291,6 +325,7 @@ def main():
291325
print(f" ✅ PR with title '{pr_title}' exists: #{pr_number} ({pr_url})")
292326
else:
293327
print(f" ❌ PR with title '{pr_title}' does not exist")
328+
print(f" Run `script/release_steps.py {toolchain} {name}` to create it")
294329
repo_status[name] = False
295330
continue
296331
print(f" ✅ On compatible toolchain (>= {toolchain})")
@@ -303,8 +338,10 @@ def main():
303338
print(f" ✅ Tag {toolchain} exists")
304339

305340
if check_stable and not is_release_candidate(toolchain):
306-
if not is_merged_into_stable(url, toolchain, "stable", github_token):
341+
if not is_merged_into_stable(url, toolchain, "stable", github_token, verbose):
342+
org_repo = extract_org_repo_from_url(url)
307343
print(f" ❌ Tag {toolchain} is not merged into stable")
344+
print(f" Run `script/merge_remote.py {org_repo} stable {toolchain}` to merge it")
308345
repo_status[name] = False
309346
continue
310347
print(f" ✅ Tag {toolchain} is merged into stable")

script/release_repos.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ repositories:
8585
- Batteries
8686
- doc-gen4
8787
- import-graph
88+
- plausible
8889

8990
- name: REPL
9091
url: https://github.com/leanprover-community/repl

script/release_steps.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,35 +51,40 @@ def find_repo(repo_substring, config):
5151
def generate_script(repo, version, config):
5252
repo_config = find_repo(repo, config)
5353
repo_name = repo_config['name']
54+
repo_url = repo_config['url']
55+
# Extract the last component of the URL, removing the .git extension if present
56+
repo_dir = repo_url.split('/')[-1].replace('.git', '')
5457
default_branch = repo_config.get("branch", "main")
5558
dependencies = repo_config.get("dependencies", [])
5659
requires_tagging = repo_config.get("toolchain-tag", True)
5760
has_stable_branch = repo_config.get("stable-branch", True)
5861

5962
script_lines = [
60-
f"cd {repo_name}",
63+
f"cd {repo_dir}",
6164
"git fetch",
62-
f"git checkout {default_branch}",
65+
f"git checkout {default_branch} && git pull",
6366
f"git checkout -b bump_to_{version}",
6467
f"echo leanprover/lean4:{version} > lean-toolchain",
6568
]
6669

6770
# Special cases for specific repositories
6871
if repo_name == "REPL":
6972
script_lines.extend([
73+
"lake update",
7074
"cd test/Mathlib",
75+
f"perl -pi -e 's/rev = \"v\\d+\\.\\d+\\.\\d+(-rc\\d+)?\"/rev = \"{version}\"/g' lakefile.toml",
7176
f"echo leanprover/lean4:{version} > lean-toolchain",
72-
'echo "Please update the dependencies in lakefile.{lean,toml}"',
7377
"lake update",
74-
"cd ../.."
78+
"cd ../..",
79+
"./test.sh"
7580
])
7681
elif dependencies:
7782
script_lines.append('echo "Please update the dependencies in lakefile.{lean,toml}"')
83+
script_lines.append("lake update")
7884

79-
script_lines.append("lake update")
8085
script_lines.append("")
8186

82-
if not re.search(r'rc\d+$', version) and repo_name in ["Batteries", "Mathlib"]:
87+
if re.search(r'rc\d+$', version) and repo_name in ["Batteries", "Mathlib"]:
8388
script_lines.extend([
8489
"echo 'This repo has nightly-testing infrastructure'",
8590
f"git merge bump/{version}",
@@ -98,13 +103,13 @@ def generate_script(repo, version, config):
98103
if repo_name == "ProofWidgets4":
99104
script_lines.append(f"echo 'Note: Follow the version convention of the repository for tagging.'")
100105
elif requires_tagging:
101-
script_lines.append(f"git tag -a {version} -m 'Release {version}'")
102-
script_lines.append("git push origin --tags")
106+
script_lines.append(f"git checkout {default_branch} && git pull")
107+
script_lines.append(f'[ "$(cat lean-toolchain)" = "leanprover/lean4:{version}" ] && git tag -a {version} -m \'Release {version}\' && git push origin --tags || echo "Error: lean-toolchain does not contain expected version {version}"')
103108

104109
if has_stable_branch:
105110
script_lines.extend([
106-
"git checkout stable",
107-
f"git merge {version}",
111+
"git checkout stable && git pull",
112+
f"git merge {version} --no-edit",
108113
"git push origin stable"
109114
])
110115

src/Init/Data/BitVec/Bitblast.lean

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1752,4 +1752,9 @@ theorem extractLsb'_mul {w len} {x y : BitVec w} (hlen : len ≤ w) :
17521752
(x * y).extractLsb' 0 len = (x.extractLsb' 0 len) * (y.extractLsb' 0 len) := by
17531753
simp [← setWidth_eq_extractLsb' hlen, setWidth_mul _ _ hlen]
17541754

1755+
/-- Adding bitvectors that are zero in complementary positions equals concatenation. -/
1756+
theorem append_add_append_eq_append {v w : Nat} {x : BitVec v} {y : BitVec w} :
1757+
(x ++ 0#w) + (0#v ++ y) = x ++ y := by
1758+
rw [add_eq_or_of_and_eq_zero] <;> ext i <;> simp
1759+
17551760
end BitVec

0 commit comments

Comments
 (0)