[ 
https://issues.apache.org/jira/browse/GROOVY-12013?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18081347#comment-18081347
 ] 

ASF GitHub Bot commented on GROOVY-12013:
-----------------------------------------

Copilot commented on code in PR #2536:
URL: https://github.com/apache/groovy/pull/2536#discussion_r3252231663


##########
subprojects/groovy-typecheckers/src/main/groovy/groovy/typecheckers/CombinerChecker.groovy:
##########
@@ -0,0 +1,344 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.typecheckers
+
+import org.apache.groovy.lang.annotation.Incubating
+import org.apache.groovy.typecheckers.CheckingVisitor
+import org.codehaus.groovy.ast.ClassNode
+import org.codehaus.groovy.ast.CodeVisitorSupport
+import org.codehaus.groovy.ast.MethodNode
+import org.codehaus.groovy.ast.expr.ArgumentListExpression
+import org.codehaus.groovy.ast.expr.BinaryExpression
+import org.codehaus.groovy.ast.expr.ClassExpression
+import org.codehaus.groovy.ast.expr.ClosureExpression
+import org.codehaus.groovy.ast.expr.ConstantExpression
+import org.codehaus.groovy.ast.expr.Expression
+import org.codehaus.groovy.ast.expr.MethodCallExpression
+import org.codehaus.groovy.ast.expr.MethodPointerExpression
+import org.codehaus.groovy.ast.expr.VariableExpression
+import org.codehaus.groovy.transform.stc.GroovyTypeCheckingExtensionSupport
+import org.codehaus.groovy.transform.stc.StaticTypesMarker
+
+/**
+ * A compile-time checker that verifies the combiner passed to an
+ * order-independent parallel reduction ({@code sumParallel},
+ * {@code injectParallel}) carries the associativity contract those methods
+ * silently require — mirroring the design of {@code PurityChecker}.
+ * <p>
+ * Associativity is undecidable in general, so this checker is deliberately
+ * conservative. It has two modes, selected via the extension option
+ * {@code mode}:
+ * <pre>
+ * // default (lenient): only flag high-confidence problems
+ * {@code @TypeChecked(extensions = 'groovy.typecheckers.CombinerChecker')}
+ *
+ * // strict: additionally require the combiner to be declared 
@Associative/@Reducer
+ * {@code @TypeChecked(extensions = "groovy.typecheckers.CombinerChecker(mode: 
'strict')")}
+ * </pre>
+ * <p>
+ * What it does, by combiner shape:
+ * <ul>
+ *   <li><b>Method reference</b> to a method annotated {@link 
groovy.transform.Associative}
+ *       or {@link groovy.transform.Reducer} &mdash; accepted.</li>
+ *   <li><b>Inline closure</b> whose combine expression applies a 
non-associative
+ *       operator ({@code -}, {@code /}, {@code %}) directly to the two 
combiner
+ *       parameters (e.g. <code>{ a, b {@literal ->} a - b }</code>) &mdash; 
flagged in
+ *       <em>any</em> mode (high confidence).</li>
+ *   <li><b>{@code injectParallel}</b> seed that contradicts a {@code 
@Reducer}'s
+ *       declared {@code zero()} (both statically constant) &mdash; 
flagged.</li>
+ *   <li>Un-annotated method references and other inline closures &mdash; 
flagged
+ *       only in <b>strict</b> mode (a declared, verifiable contract is 
required).</li>
+ * </ul>

Review Comment:
   The Javadoc says lenient mode flags only `-`, `/`, `%` for inline combiners, 
but `NON_ASSOCIATIVE_OPS` (and the tests) include `**` as well. Please update 
the Javadoc description (and/or the operator set) so the documented behavior 
matches what the checker actually enforces.



##########
subprojects/groovy-typecheckers/src/spec/doc/typecheckers.adoc:
##########
@@ -1241,3 +1250,141 @@ Both checkers can be used independently or together:
 @TypeChecked(extensions = ['groovy.typecheckers.ModifiesChecker',
                            'groovy.typecheckers.PurityChecker'])
 ----
+
+== Checking @Associative/@Reducer Combiners (Incubating)
+
+The parallel `Collection` reductions split, reorder, and recombine their input.
+That is only correct when the combining function is *associative* --
+`combine(a, combine(b, c))` must equal `combine(combine(a, b), c)` 

> New optional type checking extension: CombinerChecker to verify associative 
> combiners in injectParallel/sumParallel/Stream.reduce
> ---------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: GROOVY-12013
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12013
>             Project: Groovy
>          Issue Type: New Feature
>            Reporter: Paul King
>            Assignee: Paul King
>            Priority: Major
>
> h3. Summary
> Adds two incubating annotations and an opt-in type-checking extension that
> verify, at compile time, that the combining function passed to an
> order-independent reduction is associative — the contract such reductions
> silently require but cannot otherwise enforce.
> h3. Annotations ({{groovy.transform}})
> * {{@Associative}} — declares a two-argument combiner is associative:
>   {{combine(a, combine(b, c)) == combine(combine(a, b), c)}}.
> * {{@Reducer}} — associative *and* has an identity element, named via the
>   {{zero()}} member (e.g. {{@Reducer(zero = '0')}}).
> h3. CombinerChecker ({{groovy.typecheckers}})
> A type-checking extension enabled with
> {{@TypeChecked(extensions='groovy.typecheckers.CombinerChecker')}}.
> It inspects the combiner of:
> * Groovy's parallel collection reductions {{injectParallel}} and
>   {{sumParallel}};
> * the JDK stream reductions {{Stream}}, {{IntStream}}, {{LongStream}} and
>   {{DoubleStream}} {{reduce}} (all overloads).
> The combiner is accepted when it is:
> * a method reference to an {{@Associative}}/{{@Reducer}} method; or
> * a method reference to, or a thin delegating closure over, a {{Monoid}}
>   or {{Semigroup}} from any library (matched by simple type name —
>   Functional Java, Palatable Lambda, Purefun, etc. — configurable via the
>   {{monoids}}/{{semigroups}} options, no dependency added).
> It reports a compile-time error when:
> * an inline closure applies a non-associative operator ({{-}}, {{/}}, {{%}},
>   {{**}}) directly to its two arguments;
> * the seed of a seeded reduction contradicts a {{@Reducer}}'s declared
>   identity;
> * a {{Semigroup}} (which has no identity) is used with a seeded reduction
>   that requires one.
> Two modes: the default (lenient) flags only high-confidence problems; *strict*
> ({{CombinerChecker(mode: 'strict')}}) additionally requires the combiner to
> carry a declared, verifiable contract.
> h3. Notes and non-goals
> * The checker runs only under {{@TypeChecked}}/{{@CompileStatic}} and treats
>   the annotations/carrier types as asserted contracts (not proofs);
>   sequential {{inject}} is deliberately never flagged.
> * The inline-closure check is syntactic and assumes operators carry their
>   conventional meaning. Resolving overloaded operators is a non-goal: in
>   particular, overloading a normally-associative operator (e.g. {{+}}, {{*}})
>   to be non-associative is poor style and is not detected by design. The
>   declaration paths ({{@Associative}}/{{@Reducer}}, Monoid/Semigroup) are the
>   reliable, overloading-immune way to assert the contract.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to