# 4.0.0 (2026-XX-XX)

 * Reject an `autoescape` tag strategy that is neither a string nor `false` at compile time instead of failing at runtime
 * Remove support for cloning `Twig\Environment`; create a new environment instead
 * Remove support for calling `TemplateWrapper::unwrap()` without an environment argument
 * Stop detecting `echo` and `print` in compiled nodes; `yield` is the only supported mode and 3.x deprecates the alternative
 * Remove the now inert `#[YieldReady]` and `#[FirstClassTwigCallableReady]` attributes from the built-in nodes; the attribute classes are kept so code supporting both 3.x and 4.x passes static analysis
 * Add an optional `string $template = ''` argument to `IncludeNode::addGetTemplate()`; subclasses overriding this method must declare a compatible parameter
 * Remove the obsolete `Twig\Test\NodeTestCase::getTests()` method; override `provideTests()` instead
 * Make `Twig\Markup` final
 * Remove `getDeprecatingPackage()`, `getDeprecatedVersion()`, and `getAlternative()` from `Twig\TwigCallableInterface` and `Twig\AbstractTwigCallable`; use `Twig\AbstractTwigCallable::triggerDeprecation()` instead
 * Remove the `Twig\Test\IntegrationTestCase::getTests()` and `getLegacyTests()` data providers; use `provideTests()` and `provideLegacyTests()` instead
 * Add native return types to the protected extension points in `Twig\Test\IntegrationTestCase` and `Twig\Test\NodeTestCase` and require overriding methods to declare compatible return types
 * Remove `TemplateVariable` and `AssignTemplateVariable`; use `MacroVariable` and `AssignMacroVariable` instead
 * Add the `isAlwaysAllowedInSandbox()` method to `Twig\TwigCallableInterface` and `Twig\TokenParser\TokenParserInterface`
 * Always allow printing a `Markup` object in a sandbox, whatever the security policy is
 * Remove the `Twig\Sandbox\SourcePolicyInterface` interface and the corresponding argument of `Twig\Extension\SandboxExtension::__construct()`
 * Remove the `sandboxed` argument of the `include` function, use the `render_sandboxed` function instead
 * Remove `Twig\Extension\SandboxExtension::enableSandbox()`, `disableSandbox()`, and `isSandboxedGlobally()`, use `Twig\Sandbox\Sandbox` instead
 * Enforce the `parent`, `block`, and `attribute` functions against the sandbox `allowedFunctions` allow-list
 * Enforce tests against the sandbox `allowedTests` allow-list and add the `$allowedTests` argument to `Twig\Sandbox\SecurityPolicy::__construct()`
 * Add a fourth `array $tests` argument to `Twig\Sandbox\SecurityPolicyInterface::checkSecurity()`
 * Throw a `SyntaxError` when an `extends`, `use`, or `macro` tag is not at the root of a template
 * Throw a `SyntaxError` when a macro is defined more than once in a template
 * Throw a `SyntaxError` when calling a macro without parentheses
 * Throw a `SyntaxError` when testing a macro with the `defined` test using parentheses
 * Remove support for case-insensitive macro references
 * Remove support for resolving macros through legacy `macro_`-prefixed references
 * Remove support for omitted, extra, and unknown macro arguments unless accepted by defaults or an explicit variadic argument
 * Remove the implicit `varargs` macro variable; use an explicit variadic argument instead
 * Remove support for extending `Twig\Node\MacroNode`
 * Remove support for passing invalid node types to `Twig\Node\MacroNode` and `Twig\Node\ModuleNode`

# 4.0.0 alpha 1 (2026-05-17)

 * Make the `raw` filter sticky
 * Add back the `if` condition on `for` loops
 * Add return types to all ExtensionInterface methods (`getFunctions()`, `getFilters()`, etc.)
 * Add support for recursive loops (via the `loop()` function)
 * Add `loop.changed`, `loop.previous`, `loop.next`, and `loop.cycle` variables
 * Make `loop.last` always available (even for non-countable iterators)
 * Change the compilation of `for` loops to throw an exception when a `loop.*` variable is not defined
 * Mark `Environment::getGlobals()` as internal
 * Drop support for PHP < 8.2
 * Escape characters are no longer ignored
