formula/source/core/api/FormulaCompiler.cxx |   64 +++++-----
 include/formula/FormulaCompiler.hxx         |    9 -
 sc/inc/compiler.hxx                         |   28 ++++
 sc/source/core/data/formulacell.cxx         |   22 +++
 sc/source/core/data/grouptokenconverter.cxx |   17 --
 sc/source/core/tool/compiler.cxx            |  167 ++++++++++++++++++++--------
 sc/source/core/tool/token.cxx               |    3 
 7 files changed, 213 insertions(+), 97 deletions(-)

New commits:
commit 1bf7bc6f9929ceae0ea059b64ae0efa12228525f
Author:     Luboš Luňák <l.lu...@collabora.com>
AuthorDate: Tue Jul 24 15:28:49 2018 +0200
Commit:     Luboš Luňák <l.lu...@collabora.com>
CommitDate: Tue Jul 31 16:01:40 2018 +0200

    try to detect that a formula does not contain any implicit intersection
    
    Commit 67444cbe disabled svDoubleRef completely for OpenCL, which means
    many formulas weren't handled by OpenCL even if the implicit intersection
    problems are quite rare. This patch tries to detect formulas implicit
    intersections in formulas and if it's certain that a formula does not
    contain one, then it's ok to use OpenCL with svDoubleRef.
    
    The detection is done by having ScCompiler analyze each opcode call
    and its parameters, which should provide sufficient information to know
    if implicit intersection can take place or not. The extra compilation
    can be avoided by using OpenCL's compilation and doing the svDoubleRef
    conversion later on the RPN code, to be done later.
    
    This is opt-in, so if unsure don't do anything, if it turns out that some
    opcode needs special handling, it can be simply added.
    
    Change-Id: Iaa52fa7eb8b14dc8c2b92384a21e2ab8efe0ddd7
    Reviewed-on: https://gerrit.libreoffice.org/57959
    Tested-by: Jenkins
    Reviewed-by: Luboš Luňák <l.lu...@collabora.com>

diff --git a/formula/source/core/api/FormulaCompiler.cxx 
b/formula/source/core/api/FormulaCompiler.cxx
index c22f9edbdbe1..3329db657812 100644
--- a/formula/source/core/api/FormulaCompiler.cxx
+++ b/formula/source/core/api/FormulaCompiler.cxx
@@ -1349,6 +1349,10 @@ bool FormulaCompiler::GetToken()
                 return HandleDbData();
             case ocTableRef:
                 return HandleTableRef();
+            case ocPush:
+                if( mbComputeII )
+                    HandleIIOpCode(mpToken.get(), nullptr, 0);
+                break;
             default:
                 ;   // nothing
         }
@@ -1556,7 +1560,6 @@ void FormulaCompiler::Factor()
             else
             {
                 // standard handling of 1-parameter opcodes
-                OpCode eMyLastOp = eOp;
                 pFacToken = mpToken;
                 eOp = NextToken();
                 if( nNumFmt == SvNumFormatType::UNDEFINED && eOp == ocNot )
@@ -1574,10 +1577,10 @@ void FormulaCompiler::Factor()
                 else if ( pArr->GetCodeError() == FormulaError::NONE )
                 {
                     pFacToken->SetByte( 1 );
-                    if (mbComputeII && IsIIOpCode(eMyLastOp))
+                    if (mbComputeII)
                     {
                         FormulaToken** pArg = pCode - 1;
-                        HandleIIOpCode(eMyLastOp, 
pFacToken->GetInForceArray(), &pArg, 1);
+                        HandleIIOpCode(pFacToken, &pArg, 1);
                     }
                 }
                 PutCode( pFacToken );
@@ -1621,7 +1624,7 @@ void FormulaCompiler::Factor()
             sal_uInt32 nSepCount = 0;
             if( !bNoParam )
             {
-                bool bDoIICompute = mbComputeII && IsIIOpCode(eMyLastOp);
+                bool bDoIICompute = mbComputeII;
                 // Array of FormulaToken double pointers to collect the 
parameters of II opcodes.
                 FormulaToken*** pArgArray = nullptr;
                 if (bDoIICompute)
@@ -1648,7 +1651,7 @@ void FormulaCompiler::Factor()
                         pArgArray[nSepCount - 1] = pCode - 1; // Add rest of 
the arguments
                 }
                 if (bDoIICompute)
-                    HandleIIOpCode(eMyLastOp, pFacToken->GetInForceArray(), 
pArgArray,
+                    HandleIIOpCode(pFacToken, pArgArray,
                                    std::min(nSepCount, 
static_cast<sal_uInt32>(FORMULA_MAXPARAMSII)));
             }
             if (bBadName)
@@ -1873,10 +1876,10 @@ void FormulaCompiler::UnaryLine()
         FormulaTokenRef p = mpToken;
         NextToken();
         UnaryLine();
-        if (mbComputeII && IsIIOpCode(p->GetOpCode()))
+        if (mbComputeII)
         {
             FormulaToken** pArg = pCode - 1;
-            HandleIIOpCode(p->GetOpCode(), p->GetInForceArray(), &pArg, 1);
+            HandleIIOpCode(p.get(), &pArg, 1);
         }
         PutCode( p );
     }
@@ -1889,10 +1892,10 @@ void FormulaCompiler::PostOpLine()
     UnaryLine();
     while ( mpToken->GetOpCode() == ocPercentSign )
     {   // this operator _follows_ its operand
-        if (mbComputeII && IsIIOpCode(mpToken->GetOpCode()))
+        if (mbComputeII)
         {
             FormulaToken** pArg = pCode - 1;
-            HandleIIOpCode(mpToken->GetOpCode(), mpToken->GetInForceArray(), 
&pArg, 1);
+            HandleIIOpCode(mpToken.get(), &pArg, 1);
         }
         PutCode( mpToken );
         NextToken();
@@ -1905,16 +1908,15 @@ void FormulaCompiler::PowLine()
     while (mpToken->GetOpCode() == ocPow)
     {
         FormulaTokenRef p = mpToken;
-        bool bDoIICompute = mbComputeII && IsIIOpCode(p->GetOpCode());
         FormulaToken** pArgArray[2];
-        if (bDoIICompute)
+        if (mbComputeII)
             pArgArray[0] = pCode - 1; // Add first argument
         NextToken();
         PostOpLine();
-        if (bDoIICompute)
+        if (mbComputeII)
         {
             pArgArray[1] = pCode - 1; // Add second argument
-            HandleIIOpCode(p->GetOpCode(), p->GetInForceArray(), pArgArray, 2);
+            HandleIIOpCode(p.get(), pArgArray, 2);
         }
         PutCode(p);
     }
@@ -1926,16 +1928,15 @@ void FormulaCompiler::MulDivLine()
     while (mpToken->GetOpCode() == ocMul || mpToken->GetOpCode() == ocDiv)
     {
         FormulaTokenRef p = mpToken;
-        bool bDoIICompute = mbComputeII && IsIIOpCode(p->GetOpCode());
         FormulaToken** pArgArray[2];
-        if (bDoIICompute)
+        if (mbComputeII)
             pArgArray[0] = pCode - 1; // Add first argument
         NextToken();
         PowLine();
-        if (bDoIICompute)
+        if (mbComputeII)
         {
             pArgArray[1] = pCode - 1; // Add second argument
-            HandleIIOpCode(p->GetOpCode(), p->GetInForceArray(), pArgArray, 2);
+            HandleIIOpCode(p.get(), pArgArray, 2);
         }
         PutCode(p);
     }
@@ -1947,16 +1948,15 @@ void FormulaCompiler::AddSubLine()
     while (mpToken->GetOpCode() == ocAdd || mpToken->GetOpCode() == ocSub)
     {
         FormulaTokenRef p = mpToken;
-        bool bDoIICompute = mbComputeII && IsIIOpCode(p->GetOpCode());
         FormulaToken** pArgArray[2];
-        if (bDoIICompute)
+        if (mbComputeII)
             pArgArray[0] = pCode - 1; // Add first argument
         NextToken();
         MulDivLine();
-        if (bDoIICompute)
+        if (mbComputeII)
         {
             pArgArray[1] = pCode - 1; // Add second argument
-            HandleIIOpCode(p->GetOpCode(), p->GetInForceArray(), pArgArray, 2);
+            HandleIIOpCode(p.get(), pArgArray, 2);
         }
         PutCode(p);
     }
@@ -1968,16 +1968,15 @@ void FormulaCompiler::ConcatLine()
     while (mpToken->GetOpCode() == ocAmpersand)
     {
         FormulaTokenRef p = mpToken;
-        bool bDoIICompute = mbComputeII && IsIIOpCode(p->GetOpCode());
         FormulaToken** pArgArray[2];
-        if (bDoIICompute)
+        if (mbComputeII)
             pArgArray[0] = pCode - 1; // Add first argument
         NextToken();
         AddSubLine();
-        if (bDoIICompute)
+        if (mbComputeII)
         {
             pArgArray[1] = pCode - 1; // Add second argument
-            HandleIIOpCode(p->GetOpCode(), p->GetInForceArray(), pArgArray, 2);
+            HandleIIOpCode(p.get(), pArgArray, 2);
         }
         PutCode(p);
     }
@@ -1989,16 +1988,15 @@ void FormulaCompiler::CompareLine()
     while (mpToken->GetOpCode() >= ocEqual && mpToken->GetOpCode() <= 
ocGreaterEqual)
     {
         FormulaTokenRef p = mpToken;
-        bool bDoIICompute = mbComputeII && IsIIOpCode(p->GetOpCode());
         FormulaToken** pArgArray[2];
-        if (bDoIICompute)
+        if (mbComputeII)
             pArgArray[0] = pCode - 1; // Add first argument
         NextToken();
         ConcatLine();
-        if (bDoIICompute)
+        if (mbComputeII)
         {
             pArgArray[1] = pCode - 1; // Add second argument
-            HandleIIOpCode(p->GetOpCode(), p->GetInForceArray(), pArgArray, 2);
+            HandleIIOpCode(p.get(), pArgArray, 2);
         }
         PutCode(p);
     }
@@ -2018,16 +2016,15 @@ OpCode FormulaCompiler::Expression()
     {
         FormulaTokenRef p = mpToken;
         mpToken->SetByte( 2 );       // 2 parameters!
-        bool bDoIICompute = mbComputeII && IsIIOpCode(p->GetOpCode());
         FormulaToken** pArgArray[2];
-        if (bDoIICompute)
+        if (mbComputeII)
             pArgArray[0] = pCode - 1; // Add first argument
         NextToken();
         CompareLine();
-        if (bDoIICompute)
+        if (mbComputeII)
         {
             pArgArray[1] = pCode - 1; // Add second argument
-            HandleIIOpCode(p->GetOpCode(), p->GetInForceArray(), pArgArray, 2);
+            HandleIIOpCode(p.get(), pArgArray, 2);
         }
         PutCode(p);
     }
@@ -2101,6 +2098,7 @@ bool FormulaCompiler::CompileTokenArray()
         // Some trailing garbage that doesn't form an expression?
         if (eOp != ocStop)
             SetError( FormulaError::OperatorExpected);
+        PostProcessCode();
 
         FormulaError nErrorBeforePop = pArr->GetCodeError();
 
diff --git a/include/formula/FormulaCompiler.hxx 
b/include/formula/FormulaCompiler.hxx
index 1f0bb0d95c5b..d50a2e2f8be1 100644
--- a/include/formula/FormulaCompiler.hxx
+++ b/include/formula/FormulaCompiler.hxx
@@ -330,12 +330,13 @@ protected:
     bool MergeRangeReference( FormulaToken * * const pCode1, FormulaToken * 
const * const pCode2 );
 
     // Returns whether the opcode has implicit intersection ranges as 
parameters.
-    // This is no-op for this class.
-    virtual bool IsIIOpCode(OpCode /*nOpCode*/) const { return false; }
-    // Handles II opcode and passes the parameter array and number of 
parameters.
-    virtual void HandleIIOpCode(OpCode /*nOpCode*/, formula::ParamClass 
/*eClass*/,
+    // Called for (most) opcodes to possibly handle implicit intersection for 
the parameters.
+    virtual void HandleIIOpCode(FormulaToken* /*token*/,
                                 FormulaToken*** /*pppToken*/, sal_uInt8 
/*nNumParams*/) {}
 
+    // Called from CompileTokenArray() after RPN code generation is done.
+    virtual void PostProcessCode() {}
+
     OUString            aCorrectedFormula;      // autocorrected Formula
     OUString            aCorrectedSymbol;       // autocorrected Symbol
 
diff --git a/sc/inc/compiler.hxx b/sc/inc/compiler.hxx
index 2b10a67ef3ab..17e87347a23d 100644
--- a/sc/inc/compiler.hxx
+++ b/sc/inc/compiler.hxx
@@ -34,6 +34,7 @@
 #include <com/sun/star/i18n/ParseResult.hpp>
 #include <vector>
 #include <memory>
+#include <set>
 #include <com/sun/star/uno/Sequence.hxx>
 
 #include <formula/FormulaCompiler.hxx>
@@ -302,6 +303,21 @@ private:
     };
     std::vector<TableRefEntry> maTableRefs;     /// "stack" of currently 
active ocTableRef tokens
 
+    // Optimizing implicit intersection is done only at the end of code 
generation, because the usage context may
+    // be important. Store candidate parameters and the operation they are the 
argument for.
+    struct PendingImplicitIntersectionOptimization
+    {
+        PendingImplicitIntersectionOptimization(formula::FormulaToken** p, 
const formula::FormulaToken* o)
+            : parameter( p ), operation( o ) {}
+        formula::FormulaToken** parameter;
+        const formula::FormulaToken* operation;
+    };
+    std::vector< PendingImplicitIntersectionOptimization > 
mPendingImplicitIntersectionOptimizations;
+    std::set<formula::FormulaTokenRef> mUnhandledPossibleImplicitIntersections;
+#ifdef DBG_UTIL
+    std::set<OpCode> mUnhandledPossibleImplicitIntersectionsOpCodes;
+#endif
+
     bool   NextNewToken(bool bInArray);
 
     virtual void SetError(FormulaError nError) override;
@@ -457,6 +473,11 @@ public:
     static bool DoubleRefToPosSingleRefScalarCase(const ScRange& rRange, 
ScAddress& rAdr,
                                                   const ScAddress& 
rFormulaPos);
 
+    bool HasUnhandledPossibleImplicitIntersections() const { return 
!mUnhandledPossibleImplicitIntersections.empty(); }
+#ifdef DBG_UTIL
+    const std::set<OpCode>& UnhandledPossibleImplicitIntersectionsOpCodes() { 
return mUnhandledPossibleImplicitIntersectionsOpCodes; }
+#endif
+
 private:
     // FormulaCompiler
     virtual OUString FindAddInFunction( const OUString& rUpperName, bool 
bLocalFirst ) const override;
@@ -485,8 +506,11 @@ private:
     ScCharFlags GetCharTableFlags( sal_Unicode c, sal_Unicode cLast )
         { return c < 128 ? pConv->getCharTableFlags(c, cLast) : 
ScCharFlags::NONE; }
 
-    bool IsIIOpCode(OpCode nOpCode) const override;
-    void HandleIIOpCode(OpCode nOpCode, formula::ParamClass eClass, 
formula::FormulaToken*** pppToken, sal_uInt8 nNumParams) override;
+    virtual void HandleIIOpCode(formula::FormulaToken* token, 
formula::FormulaToken*** pppToken, sal_uInt8 nNumParams) override;
+    bool HandleIIOpCodeInternal(formula::FormulaToken* token, 
formula::FormulaToken*** pppToken, sal_uInt8 nNumParams);
+    bool SkipImplicitIntersectionOptimization(const formula::FormulaToken* 
token) const;
+    virtual void PostProcessCode() override;
+    static bool ParameterMayBeImplicitIntersection(const 
formula::FormulaToken* token, int parameter);
     void ReplaceDoubleRefII(formula::FormulaToken** ppDoubleRefTok);
     bool AdjustSumRangeShape(const ScComplexRefData& rBaseRange, 
ScComplexRefData& rSumRange);
     void CorrectSumRange(const ScComplexRefData& rBaseRange, ScComplexRefData& 
rSumRange, formula::FormulaToken** ppSumRangeToken);
diff --git a/sc/source/core/data/formulacell.cxx 
b/sc/source/core/data/formulacell.cxx
index 112d215bc2f7..9ff780be7e6a 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -68,6 +68,7 @@
 #include <listenerqueryids.hxx>
 #include <grouparealistener.hxx>
 #include <formulalogger.hxx>
+#include <com/sun/star/sheet/FormulaLanguage.hpp>
 
 #if HAVE_FEATURE_OPENCL
 #include <opencl/openclwrapper.hxx>
@@ -4788,9 +4789,26 @@ bool 
ScFormulaCell::InterpretFormulaGroupOpenCL(sc::FormulaLogger::GroupScope& a
 
         ScTokenArray aCode;
         ScGroupTokenConverter aConverter(aCode, *pDocument, *this, 
xGroup->mpTopCell->aPos);
-        if (!aConverter.convert(*pCode, aScope))
+        // TODO avoid this extra compilation
+        ScCompiler aComp( pDocument, xGroup->mpTopCell->aPos, *pCode, 
formula::FormulaGrammar::GRAM_UNSPECIFIED, true, cMatrixFlag != 
ScMatrixMode::NONE );
+        aComp.CompileTokenArray();
+        if (aComp.HasUnhandledPossibleImplicitIntersections() || 
!aConverter.convert(*pCode, aScope))
         {
-            SAL_INFO("sc.opencl", "conversion of group " << this << " failed, 
disabling");
+            if(aComp.HasUnhandledPossibleImplicitIntersections())
+            {
+                SAL_INFO("sc.opencl", "group " << xGroup->mpTopCell->aPos << " 
has unhandled implicit intersections, disabling");
+#ifdef DBG_UTIL
+                for( const OpCode opcode : 
aComp.UnhandledPossibleImplicitIntersectionsOpCodes())
+                {
+                    SAL_INFO("sc.opencl", "unhandled implicit intersection 
opcode "
+                        << 
formula::FormulaCompiler().GetOpCodeMap(com::sun::star::sheet::FormulaLanguage::ENGLISH)->getSymbol(opcode)
+                        << "(" << int(opcode) << ")");
+                }
+#endif
+            }
+            else
+                SAL_INFO("sc.opencl", "conversion of group " << 
xGroup->mpTopCell->aPos << " failed, disabling");
+
             mxGroup->meCalcState = sc::GroupCalcDisabled;
 
             // Undo the hack above
diff --git a/sc/source/core/data/grouptokenconverter.cxx 
b/sc/source/core/data/grouptokenconverter.cxx
index a388b34d04ec..c5e99737afab 100644
--- a/sc/source/core/data/grouptokenconverter.cxx
+++ b/sc/source/core/data/grouptokenconverter.cxx
@@ -167,18 +167,12 @@ bool ScGroupTokenConverter::convert( const ScTokenArray& 
rCode, sc::FormulaLogge
             break;
             case svDoubleRef:
             {
-                /* FIXME: this simply does not work, it doesn't know
-                 * a) the context of implicit intersection, for which creating
-                      two arrays does not only result in huge unnecessary 
matrix
-                      operations but also produces wrong results, e.g. =B:B/C:C
-                 * b) when to keep a reference as a reference depending on the
-                      expected parameter type, e.g. INDEX(), OFFSET() and
-                      others (though that *may* be disabled by OpCode already).
-                 * Until both are solved keep the reference. */
-                mrGroupTokens.AddToken(*p);
-                break;
+                // This code may break in case of implicit intersection, 
leading to unnecessarily large
+                // matrix operations and possibly incorrect results 
(=C:C/D:D). That is handled by
+                // having ScCompiler check that there are no possible implicit 
intersections.
+                // Additionally some functions such as INDEX() and OFFSET() 
require a reference,
+                // that is handled by blacklisting those opcodes in 
ScTokenArray::CheckToken().
 
-#if 0
                 ScComplexRefData aRef = *p->GetDoubleRef();
                 if( aRef.IsDeleted())
                     return false;
@@ -253,7 +247,6 @@ bool ScGroupTokenConverter::convert( const ScTokenArray& 
rCode, sc::FormulaLogge
                     //ensure that backing storage exists for our lifetime
                     mxFormulaGroupContext = mrDoc.GetFormulaGroupContext();
                 }
-#endif
             }
             break;
             case svIndex:
diff --git a/sc/source/core/tool/compiler.cxx b/sc/source/core/tool/compiler.cxx
index ab5920a61aed..895a9a9f2994 100644
--- a/sc/source/core/tool/compiler.cxx
+++ b/sc/source/core/tool/compiler.cxx
@@ -5798,40 +5798,67 @@ formula::ParamClass ScCompiler::GetForceArrayParameter( 
const formula::FormulaTo
     return ScParameterClassification::GetParameterType( pToken, nParam);
 }
 
-bool ScCompiler::IsIIOpCode(OpCode nOpCode) const
+bool ScCompiler::ParameterMayBeImplicitIntersection(const FormulaToken* token, 
int parameter)
 {
-    if (nOpCode == ocSumIf || nOpCode == ocAverageIf || (nOpCode >= 
SC_OPCODE_START_1_PAR && nOpCode < SC_OPCODE_STOP_1_PAR)
-        || (nOpCode >= SC_OPCODE_START_BIN_OP && nOpCode < SC_OPCODE_STOP_UN_OP
-             && nOpCode != ocIntersect && nOpCode != ocUnion && nOpCode != 
ocRange
-             && nOpCode != ocAnd && nOpCode != ocOr && nOpCode != ocXor )
-        || nOpCode == ocPercentSign)
+    formula::ParamClass param = ScParameterClassification::GetParameterType( 
token, parameter );
+    return param == Value || param == Array;
+}
+
+bool ScCompiler::SkipImplicitIntersectionOptimization(const FormulaToken* 
token) const
+{
+    if (mbMatrixFlag)
+        return true;
+    formula::ParamClass paramClass = token->GetInForceArray();
+    if (paramClass == formula::ForceArray
+        || paramClass == formula::ReferenceOrForceArray
+        || paramClass == formula::SuppressedReferenceOrForceArray)
     {
         return true;
     }
-
+    formula::ParamClass returnType = 
ScParameterClassification::GetParameterType( token, SAL_MAX_UINT16 );
+    if( returnType == formula::Reference )
+        return true;
     return false;
 }
 
-void ScCompiler::HandleIIOpCode(OpCode nOpCode, formula::ParamClass eClass, 
FormulaToken*** pppToken, sal_uInt8 nNumParams)
+void ScCompiler::HandleIIOpCode(FormulaToken* token, FormulaToken*** pppToken, 
sal_uInt8 nNumParams)
 {
     if (!mbComputeII)
         return;
+#ifdef DBG_UTIL
+    if(!HandleIIOpCodeInternal(token, pppToken, nNumParams))
+        
mUnhandledPossibleImplicitIntersectionsOpCodes.insert(token->GetOpCode());
+#else
+    HandleIIOpCodeInternal(token, pppToken, nNumParams);
+#endif
+}
 
-    if (nOpCode == ocSumIf || nOpCode == ocAverageIf)
+// return true if opcode is handled
+bool ScCompiler::HandleIIOpCodeInternal(FormulaToken* token, FormulaToken*** 
pppToken, sal_uInt8 nNumParams)
+{
+    const OpCode nOpCode = token->GetOpCode();
+
+    if (nOpCode == ocPush)
+    {
+        if(token->GetType() == svDoubleRef)
+            mUnhandledPossibleImplicitIntersections.insert( token );
+        return true;
+    }
+    else if (nOpCode == ocSumIf || nOpCode == ocAverageIf)
     {
         if (nNumParams != 3)
-            return;
+            return false;
 
         if (!(pppToken[0] && pppToken[2] && *pppToken[0] && *pppToken[2]))
-            return;
+            return false;
 
         if ((*pppToken[0])->GetType() != svDoubleRef)
-            return;
+            return false;
 
         const StackVar eSumRangeType = (*pppToken[2])->GetType();
 
         if ( eSumRangeType != svSingleRef && eSumRangeType != svDoubleRef )
-            return;
+            return false;
 
         const ScComplexRefData& rBaseRange = *(*pppToken[0])->GetDoubleRef();
 
@@ -5845,57 +5872,113 @@ void ScCompiler::HandleIIOpCode(OpCode nOpCode, 
formula::ParamClass eClass, Form
             aSumRange = *(*pppToken[2])->GetDoubleRef();
 
         CorrectSumRange(rBaseRange, aSumRange, pppToken[2]);
+        // TODO mark parameters as handled
+        return true;
     }
     else if (nOpCode >= SC_OPCODE_START_1_PAR && nOpCode < 
SC_OPCODE_STOP_1_PAR)
     {
         if (nNumParams != 1)
-            return;
-
-        // NOTE: eClass is the CurrentFactor token's GetInForceArray() state,
-        // not the function's parameter classification.
-        if (mbMatrixFlag ||
-                eClass == formula::ForceArray ||
-                eClass == formula::ReferenceOrForceArray ||
-                eClass == formula::SuppressedReferenceOrForceArray)
-            return;
+            return false;
 
-        /* TODO: this assumes that there is no function taking a single
-         * parameter that does evaluate a range reference. There currently
-         * isn't any, but we should rather check that. */
+        if( !ParameterMayBeImplicitIntersection( token, 0 ))
+            return false;
+        if (SkipImplicitIntersectionOptimization(token))
+            return false;
 
         if ((*pppToken[0])->GetType() != svDoubleRef)
-            return;
+            return false;
 
-        ReplaceDoubleRefII(pppToken[0]);
+        mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[0], 
token );
+        return true;
     }
-    else if (nOpCode >= SC_OPCODE_START_BIN_OP && nOpCode < 
SC_OPCODE_STOP_BIN_OP)
+    else if ((nOpCode >= SC_OPCODE_START_BIN_OP && nOpCode < 
SC_OPCODE_STOP_BIN_OP)
+              || nOpCode == ocRound || nOpCode == ocRoundUp || nOpCode == 
ocRoundDown)
     {
-        assert(nOpCode != ocIntersect && nOpCode != ocUnion && nOpCode != 
ocRange);
-
         if (nNumParams != 2)
-            return;
+            return false;
 
-        if (eClass == formula::ForceArray || mbMatrixFlag)
-            return;
+        if( !ParameterMayBeImplicitIntersection( token, 0 ) || 
!ParameterMayBeImplicitIntersection( token, 1 ))
+            return false;
+        if (SkipImplicitIntersectionOptimization(token))
+            return false;
 
-        // Convert only if the other parameter is not a matrix nor an array 
(which would force the result to be a matrix).
-        if ((*pppToken[0])->GetType() == svDoubleRef && 
(*pppToken[1])->GetType() != svMatrix && (*pppToken[1])->IsInForceArray())
-            ReplaceDoubleRefII(pppToken[0]);
-        if ((*pppToken[1])->GetType() == svDoubleRef && 
(*pppToken[0])->GetType() != svMatrix && (*pppToken[0])->IsInForceArray())
-            ReplaceDoubleRefII(pppToken[1]);
+        // Convert only if the other parameter is not a matrix (which would 
force the result to be a matrix).
+        if ((*pppToken[0])->GetType() == svDoubleRef && 
(*pppToken[1])->GetType() != svMatrix)
+            mPendingImplicitIntersectionOptimizations.emplace_back( 
pppToken[0], token );
+        if ((*pppToken[1])->GetType() == svDoubleRef && 
(*pppToken[0])->GetType() != svMatrix)
+            mPendingImplicitIntersectionOptimizations.emplace_back( 
pppToken[1], token );
+        return true;
     }
     else if ((nOpCode >= SC_OPCODE_START_UN_OP && nOpCode < 
SC_OPCODE_STOP_UN_OP)
               || nOpCode == ocPercentSign)
     {
         if (nNumParams != 1)
-            return;
+            return false;
 
-        if (eClass == formula::ForceArray || mbMatrixFlag)
-            return;
+        if( !ParameterMayBeImplicitIntersection( token, 0 ))
+            return false;
+        if (SkipImplicitIntersectionOptimization(token))
+            return false;
+
+        if ((*pppToken[0])->GetType() == svDoubleRef)
+            mPendingImplicitIntersectionOptimizations.emplace_back( 
pppToken[0], token );
+        return true;
+    }
+    else if (nOpCode == ocVLookup)
+    {
+        if (nNumParams != 3 && nNumParams != 4)
+            return false;
 
+        if (SkipImplicitIntersectionOptimization(token))
+            return false;
+
+        assert( ParameterMayBeImplicitIntersection( token, 0 ));
+        assert( !ParameterMayBeImplicitIntersection( token, 1 ));
+        assert( ParameterMayBeImplicitIntersection( token, 2 ));
+        assert( ParameterMayBeImplicitIntersection( token, 3 ));
+        if ((*pppToken[2])->GetType() == svDoubleRef)
+            mPendingImplicitIntersectionOptimizations.emplace_back( 
pppToken[2], token );
         if ((*pppToken[0])->GetType() == svDoubleRef)
-            ReplaceDoubleRefII(pppToken[0]);
+            mPendingImplicitIntersectionOptimizations.emplace_back( 
pppToken[0], token );
+        if (nNumParams == 4 && (*pppToken[3])->GetType() == svDoubleRef)
+            mPendingImplicitIntersectionOptimizations.emplace_back( 
pppToken[3], token );
+        // a range for the second parameters is not an implicit intersection
+        mUnhandledPossibleImplicitIntersections.erase( *pppToken[ 1 ] );
+        return true;
+    }
+    else
+    {
+        bool possibleII = false;
+        for( int i = 0; i < nNumParams; ++i )
+        {
+            if( ParameterMayBeImplicitIntersection( token, i ))
+            {
+                possibleII = true;
+                break;
+            }
+        }
+        if( !possibleII )
+        {
+            // all parameters have been handled, they are not implicit 
intersections
+            for( int i = 0; i < nNumParams; ++i )
+                mUnhandledPossibleImplicitIntersections.erase( *pppToken[ i ] 
);
+            return true;
+        }
+    }
+
+    return false;
+}
+
+void ScCompiler::PostProcessCode()
+{
+    for( const PendingImplicitIntersectionOptimization& item : 
mPendingImplicitIntersectionOptimizations )
+    {
+        // E.g. "SUMPRODUCT(I5:I6+1)" shouldn't do implicit intersection.
+        if( item.operation->IsInForceArray())
+            continue;
+        ReplaceDoubleRefII( item.parameter );
     }
+    mPendingImplicitIntersectionOptimizations.clear();
 }
 
 void ScCompiler::ReplaceDoubleRefII(FormulaToken** ppDoubleRefTok)
diff --git a/sc/source/core/tool/token.cxx b/sc/source/core/tool/token.cxx
index f16b5339f266..760e0652f8f2 100644
--- a/sc/source/core/tool/token.cxx
+++ b/sc/source/core/tool/token.cxx
@@ -1599,11 +1599,10 @@ void ScTokenArray::CheckToken( const FormulaToken& r )
                 // Don't change the state.
             break;
             case svSingleRef:
+            case svDoubleRef:
                 // Depends on the reference state.
                 meVectorState = FormulaVectorCheckReference;
             break;
-            case svDoubleRef:
-                // Does not work yet, see ScGroupTokenConverter::convert()
             case svError:
             case svEmptyCell:
             case svExternal:
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to