Skip to content

Conversation

@oflatt
Copy link
Member

@oflatt oflatt commented Dec 2, 2025

Copilot AI review requested due to automatic review settings December 2, 2025 21:42
@oflatt oflatt requested a review from a team as a code owner December 2, 2025 21:42
@oflatt oflatt requested review from FTRobbin and removed request for a team December 2, 2025 21:42
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces the unstable-fresh! macro for egglog-experimental, which enables the generation of fresh values in rewrite rules. The macro desugars into a generated constructor function that captures query context and produces unique values based on the rule's matched expressions and an incremental counter.

Key changes:

  • Implements FreshMacro command macro with proper type-checking integration
  • Adds comprehensive test coverage through both Rust tests and .egg test files
  • Changes dependencies from git URLs to local paths for development purposes

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
src/fresh_macro.rs Core implementation of the unstable-fresh! macro with typechecking support
src/fresh_macro_typed.rs Unused alternative implementation that should be removed
src/lib.rs Registers the fresh macro and reorganizes parser setup, but accidentally removes run-schedule command registration
Cargo.toml Changes dependencies to local paths (should be reverted before merge)
Cargo.lock Updates lock file to reflect local dependencies
tests/test_fresh.rs Comprehensive Rust integration tests for fresh macro functionality
tests/test_fresh.egg Test case verifying fresh value uniqueness
tests/test_simple_fresh.egg Basic fresh macro test case
tests/test_working_fresh.egg Test case with multiple check statements

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

@yihozhang yihozhang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some comments mostly about things that could be drastically simplified using visit_exprs and subst. Also followed up on Slack about a Skolemization-related question.

Are the failing tests only those related to resugaring? Could you describe what caused the failure again? I think I am fine with disabling resugaring tests, since they are nice to have but don't exhibit a real use case right now.

}
}

fn collect_fresh_sorts(actions: &[Action]) -> Vec<Symbol> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need contains_fresh_* if we already have collect_fresh_*

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be implementable with Actions::visit_exprs

fn collect_fresh_from_expr(expr: &Expr, sorts: &mut Vec<Symbol>) {
match expr {
Expr::Call(_span, head, args) if head.as_str() == "unstable-fresh!" => {
if args.len() == 1 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if args.len() != 1, or the argument is not a variable? Will an error be thrown somewhere?

) -> Result<Vec<Command>, Error> {
// Collect unstable-fresh! sorts from actions
let fresh_sorts = collect_fresh_sorts(&rule.head.0);
if fresh_sorts.is_empty() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems redundant since the assumption was this rule contains fresh!

let rule_span = rule.span.clone();

// Typecheck the query to get resolved facts with type information
let resolved_facts = type_info.typecheck_facts(symbol_gen, &rule.body)?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: In some corner cases, queries in rules don't have unique types. It needs to be inferred together with the actions. For example:

(rule (
  (= x (new-vec))
) (
  (R x)
))

}

// Convert ResolvedExpr to surface Expr
fn resolved_to_surface(expr: &ResolvedExpr) -> Expr {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be implemented using Expr::subst, similar to Fact::make_unresolved in egglog main.

}

// Collect all unique subexpressions from resolved facts with their types
fn collect_query_columns(resolved_facts: &[ResolvedFact]) -> IndexSet<(Expr, Symbol)> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be simplified with visit_exprs?

output: output_sort,
},
cost: None,
unextractable: true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, not sure about unextractability.. This doesn't matter for eggcc, but for chase-like applications the skolem term might be the sole e-node in its e-class, and we want to print tuples with skolem terms. On the other hand the term would look ugly

}
}

fn rewrite_fresh_action(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

visit_exprs

@saulshanabrook
Copy link
Member

Yeah I also agree that disabling sugaring tests could be OK for now here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants