Resolves: tdf#120895 new ParamClass::ReferenceOrRefArray, tdf#58874 related
Too many side conditions are possible with implicit array of
references in array mode. Propagate new ReferenceOrRefArray
parameter class to indicate the preferred return type for
arguments to functions whose parameters explicitly handle array of
references.
Change-Id: I1f01266495c2ef1941ffe0cb7c2e0a5ae0bb7e69
Reviewed-on: https://gerrit.libreoffice.org/63201
Reviewed-by: Eike Rathke <erack@redhat.com>
Tested-by: Jenkins
diff --git a/formula/source/core/api/FormulaCompiler.cxx b/formula/source/core/api/FormulaCompiler.cxx
index b0938aa..ccf4562 100644
--- a/formula/source/core/api/FormulaCompiler.cxx
+++ b/formula/source/core/api/FormulaCompiler.cxx
@@ -2693,6 +2693,13 @@ formula::ParamClass FormulaCompiler::GetForceArrayParameter( const FormulaToken*
void FormulaCompiler::ForceArrayOperator( FormulaTokenRef const & rCurr )
{
if (rCurr->GetInForceArray() != ParamClass::Unknown)
// Already set, unnecessary to evaluate again. This happens by calls to
// CurrentFactor::operator=() while descending through Factor() and
// then ascending back (and down and up, ...),
// CheckSetForceArrayParameter() and later PutCode().
return;
if (!pCurrentFactorToken || (pCurrentFactorToken.get() == rCurr.get()))
return;
@@ -2700,27 +2707,37 @@ void FormulaCompiler::ForceArrayOperator( FormulaTokenRef const & rCurr )
return;
// Inherited parameter class.
formula::ParamClass eType = pCurrentFactorToken->GetInForceArray();
if (eType == formula::ParamClass::ForceArray)
const formula::ParamClass eForceType = pCurrentFactorToken->GetInForceArray();
if (eForceType == ParamClass::ForceArray || eForceType == ParamClass::ReferenceOrRefArray)
{
rCurr->SetInForceArray( eType);
// ReferenceOrRefArray was set only if in ForceArray context already,
// it is valid for the one function only to indicate the preferred
// return type. Propagate as ForceArray if not another parameter
// handling ReferenceOrRefArray.
if (nCurrentFactorParam > 0
&& (GetForceArrayParameter( pCurrentFactorToken.get(), static_cast<sal_uInt16>(nCurrentFactorParam - 1))
== ParamClass::ReferenceOrRefArray))
rCurr->SetInForceArray( ParamClass::ReferenceOrRefArray);
else
rCurr->SetInForceArray( ParamClass::ForceArray);
return;
}
else if (eType == formula::ParamClass::ReferenceOrForceArray)
else if (eForceType == ParamClass::ReferenceOrForceArray)
{
// Inherit further only if the return class of the nested function is
// not Reference. Else flag as suppressed.
if (GetForceArrayParameter( rCurr.get(), SAL_MAX_UINT16) != ParamClass::Reference)
rCurr->SetInForceArray( eType);
rCurr->SetInForceArray( eForceType);
else
rCurr->SetInForceArray( formula::ParamClass::SuppressedReferenceOrForceArray);
rCurr->SetInForceArray( ParamClass::SuppressedReferenceOrForceArray);
return;
}
if (nCurrentFactorParam > 0)
{
// Actual current parameter's class.
eType = GetForceArrayParameter( pCurrentFactorToken.get(), static_cast<sal_uInt16>(nCurrentFactorParam - 1));
const formula::ParamClass eType = GetForceArrayParameter(
pCurrentFactorToken.get(), static_cast<sal_uInt16>(nCurrentFactorParam - 1));
if (eType == ParamClass::ForceArray)
rCurr->SetInForceArray( eType);
else if (eType == ParamClass::ReferenceOrForceArray)
diff --git a/formula/source/core/api/token.cxx b/formula/source/core/api/token.cxx
index cefab32..aa3dbec 100644
--- a/formula/source/core/api/token.cxx
+++ b/formula/source/core/api/token.cxx
@@ -150,7 +150,8 @@ bool FormulaToken::IsRef() const
bool FormulaToken::IsInForceArray() const
{
ParamClass eParam = GetInForceArray();
return eParam == ParamClass::ForceArray || eParam == ParamClass::ReferenceOrForceArray;
return eParam == ParamClass::ForceArray || eParam == ParamClass::ReferenceOrForceArray
|| eParam == ParamClass::ReferenceOrRefArray;
}
bool FormulaToken::operator==( const FormulaToken& rToken ) const
diff --git a/formula/source/ui/dlg/formula.cxx b/formula/source/ui/dlg/formula.cxx
index c324be6..55135eb 100644
--- a/formula/source/ui/dlg/formula.cxx
+++ b/formula/source/ui/dlg/formula.cxx
@@ -753,6 +753,7 @@ void FormulaDlg_Impl::MakeTree( StructPage* _pTree, SvTreeListEntry* pParent, co
aUnforcedResult.clear();
break;
case ParamClass::Reference:
case ParamClass::ReferenceOrRefArray:
case ParamClass::Array:
case ParamClass::ForceArray:
case ParamClass::ReferenceOrForceArray:
diff --git a/include/formula/paramclass.hxx b/include/formula/paramclass.hxx
index a22854f..a1e5648 100644
--- a/include/formula/paramclass.hxx
+++ b/include/formula/paramclass.hxx
@@ -36,6 +36,14 @@ namespace formula
PopDoubleRefOrSingleRef() should not have this. */
Reference,
/** Like Reference but the function accepts also a list of references
(ocUnion svRefList) as one argument AND handles the special case of
an array of references in array mode. Then the resulting argument
for a parameter in JumpMatrix context may be an array of references
which then is to be preferred over a result matrix. This final
behaviour is the opposite of SuppressedReferenceOrForceArray. */
ReferenceOrRefArray,
/** In array formula: convert area reference to array. Function will be
called only once if no Value type is involved. Functions able to
handle a svMatrix parameter but not a formula::svDoubleRef parameter as area
diff --git a/sc/source/core/tool/compiler.cxx b/sc/source/core/tool/compiler.cxx
index 3c86ad2..556cd49 100644
--- a/sc/source/core/tool/compiler.cxx
+++ b/sc/source/core/tool/compiler.cxx
@@ -5809,7 +5809,8 @@ bool ScCompiler::SkipImplicitIntersectionOptimization(const FormulaToken* token)
formula::ParamClass paramClass = token->GetInForceArray();
if (paramClass == formula::ForceArray
|| paramClass == formula::ReferenceOrForceArray
|| paramClass == formula::SuppressedReferenceOrForceArray)
|| paramClass == formula::SuppressedReferenceOrForceArray
|| paramClass == formula::ReferenceOrRefArray)
{
return true;
}
diff --git a/sc/source/core/tool/interpr1.cxx b/sc/source/core/tool/interpr1.cxx
index 38e56de..b834062 100644
--- a/sc/source/core/tool/interpr1.cxx
+++ b/sc/source/core/tool/interpr1.cxx
@@ -835,8 +835,10 @@ bool ScInterpreter::JumpMatrix( short nStackLevel )
{ // We're done with it, throw away jump matrix, keep result.
// For an intermediate result of Reference use the array of references
// if there are more than one reference and the current ForceArray
// context is not ForceArray or related, suppressed, ...,
// else (also for a final result of Reference) use the matrix.
// context is not ForceArray or suppressed. Note that also
// ReferenceOrRefArray forces the array of references as result if
// there is more than one reference.
// Else (also for a final result of Reference) use the matrix.
// Treat the result of a jump command as final and use the matrix (see
// tdf#115493 for why).
ParamClass eParamClass;
diff --git a/sc/source/core/tool/interpr4.cxx b/sc/source/core/tool/interpr4.cxx
index a1eb180..3bb3cf8 100644
--- a/sc/source/core/tool/interpr4.cxx
+++ b/sc/source/core/tool/interpr4.cxx
@@ -1490,6 +1490,7 @@ bool ScInterpreter::ConvertMatrixParameters()
{
formula::ParamClass eType = ScParameterClassification::GetParameterType( pCur, nParams - i);
if ( eType != formula::ParamClass::Reference &&
eType != formula::ParamClass::ReferenceOrRefArray &&
eType != formula::ParamClass::ReferenceOrForceArray &&
// For scalar Value: convert to Array/JumpMatrix
// only if in array formula context, else (function
@@ -1554,6 +1555,7 @@ bool ScInterpreter::ConvertMatrixParameters()
{
formula::ParamClass eType = ScParameterClassification::GetParameterType( pCur, nParams - i);
if ( eType != formula::ParamClass::Reference &&
eType != formula::ParamClass::ReferenceOrRefArray &&
eType != formula::ParamClass::ReferenceOrForceArray &&
eType != formula::ParamClass::ForceArray)
{
diff --git a/sc/source/core/tool/parclass.cxx b/sc/source/core/tool/parclass.cxx
index 9f79da7..46179fe 100644
--- a/sc/source/core/tool/parclass.cxx
+++ b/sc/source/core/tool/parclass.cxx
@@ -99,25 +99,25 @@ const ScParameterClassification::RawData ScParameterClassification::pRawData[] =
{ ocTableOp, {{ Value, Value, Value, Value, Value }, 0, Value }},
// Operators and functions.
{ ocAdd, {{ Array, Array }, 0, Value }},
{ ocAggregate, {{ Value, Value, Reference }, 1, Value }},
{ ocAggregate, {{ Value, Value, ReferenceOrRefArray }, 1, Value }},
{ ocAmpersand, {{ Array, Array }, 0, Value }},
{ ocAnd, {{ Reference }, 1, Value }},
{ ocAreas, {{ Reference }, 0, Value }},
{ ocAveDev, {{ Reference }, 1, Value }},
{ ocAverage, {{ Reference }, 1, Value }},
{ ocAverageA, {{ Reference }, 1, Value }},
{ ocAverageIf, {{ Reference, Value, Reference }, 0, Value }},
{ ocAverageIfs, {{ Reference, Reference, Value }, 2, Value }},
{ ocAverage, {{ ReferenceOrRefArray }, 1, Value }},
{ ocAverageA, {{ ReferenceOrRefArray }, 1, Value }},
{ ocAverageIf, {{ ReferenceOrRefArray, Value, Reference }, 0, Value }},
{ ocAverageIfs, {{ ReferenceOrRefArray, ReferenceOrRefArray, Value }, 2, Value }},
{ ocCell, {{ Value, Reference }, 0, Value }},
{ ocColumn, {{ Reference }, 0, Value }},
{ ocColumns, {{ Reference }, 1, Value }},
{ ocConcat_MS, {{ Reference }, 1, Value }},
{ ocCorrel, {{ ForceArray, ForceArray }, 0, Value }},
{ ocCount, {{ Reference }, 1, Value }},
{ ocCount2, {{ Reference }, 1, Value }},
{ ocCountEmptyCells, {{ Reference }, 0, Value }},
{ ocCountIf, {{ Reference, Value }, 0, Value }},
{ ocCountIfs, {{ Reference, Value }, 2, Value }},
{ ocCount, {{ ReferenceOrRefArray }, 1, Value }},
{ ocCount2, {{ ReferenceOrRefArray }, 1, Value }},
{ ocCountEmptyCells, {{ ReferenceOrRefArray }, 0, Value }},
{ ocCountIf, {{ ReferenceOrRefArray, Value }, 0, Value }},
{ ocCountIfs, {{ ReferenceOrRefArray, Value }, 2, Value }},
{ ocCovar, {{ ForceArray, ForceArray }, 0, Value }},
{ ocCovarianceP, {{ ForceArray, ForceArray }, 0, Value }},
{ ocCovarianceS, {{ ForceArray, ForceArray }, 0, Value }},
@@ -176,11 +176,13 @@ const ScParameterClassification::RawData ScParameterClassification::pRawData[] =
{ ocMatTrans, {{ Array }, 0, Value }}, // strange, but Xcl doesn't force MatTrans array
{ ocMatValue, {{ Reference, Value, Value }, 0, Value }},
{ ocMatch, {{ Value, ReferenceOrForceArray, Value }, 0, Value }},
{ ocMax, {{ Reference }, 1, Value }},
{ ocMaxA, {{ Reference }, 1, Value }},
{ ocMax, {{ ReferenceOrRefArray }, 1, Value }},
{ ocMaxA, {{ ReferenceOrRefArray }, 1, Value }},
{ ocMaxIfs_MS, {{ ReferenceOrRefArray, ReferenceOrRefArray, Value }, 2, Value }},
{ ocMedian, {{ Reference }, 1, Value }},
{ ocMin, {{ Reference }, 1, Value }},
{ ocMinA, {{ Reference }, 1, Value }},
{ ocMin, {{ ReferenceOrRefArray }, 1, Value }},
{ ocMinA, {{ ReferenceOrRefArray }, 1, Value }},
{ ocMinIfs_MS, {{ ReferenceOrRefArray, ReferenceOrRefArray, Value }, 2, Value }},
{ ocModalValue, {{ ForceArray }, 1, Value }},
{ ocModalValue_MS, {{ ForceArray }, 1, Value }},
{ ocModalValue_Multi,{{ ForceArray }, 1, Value }},
@@ -206,7 +208,7 @@ const ScParameterClassification::RawData ScParameterClassification::pRawData[] =
{ ocPow, {{ Array, Array }, 0, Value }},
{ ocPower, {{ Array, Array }, 0, Value }},
{ ocProb, {{ ForceArray, ForceArray, Value, Value }, 0, Value }},
{ ocProduct, {{ Reference }, 1, Value }},
{ ocProduct, {{ ReferenceOrRefArray }, 1, Value }},
{ ocQuartile, {{ Reference, Value }, 0, Value }},
{ ocQuartile_Exc, {{ Reference, Value }, 0, Value }},
{ ocQuartile_Inc, {{ Reference, Value }, 0, Value }},
@@ -231,12 +233,12 @@ const ScParameterClassification::RawData ScParameterClassification::pRawData[] =
{ ocStDevP_MS, {{ Reference }, 1, Value }},
{ ocStDevS, {{ Reference }, 1, Value }},
{ ocSub, {{ Array, Array }, 0, Value }},
{ ocSubTotal, {{ Value, Reference }, 1, Value }},
{ ocSum, {{ Reference }, 1, Value }},
{ ocSumIf, {{ Reference, Value, Reference }, 0, Value }},
{ ocSumIfs, {{ Reference, Reference, Value }, 2, Value }},
{ ocSubTotal, {{ Value, ReferenceOrRefArray }, 1, Value }},
{ ocSum, {{ ReferenceOrRefArray }, 1, Value }},
{ ocSumIf, {{ ReferenceOrRefArray, Value, Reference }, 0, Value }},
{ ocSumIfs, {{ ReferenceOrRefArray, ReferenceOrRefArray, Value }, 2, Value }},
{ ocSumProduct, {{ ForceArray }, 1, Value }},
{ ocSumSQ, {{ Reference }, 1, Value }},
{ ocSumSQ, {{ ReferenceOrRefArray }, 1, Value }},
{ ocSumX2DY2, {{ ForceArray, ForceArray }, 0, Value }},
{ ocSumX2MY2, {{ ForceArray, ForceArray }, 0, Value }},
{ ocSumXMY2, {{ ForceArray, ForceArray }, 0, Value }},
@@ -246,10 +248,10 @@ const ScParameterClassification::RawData ScParameterClassification::pRawData[] =
{ ocTrimMean, {{ Reference, Value }, 0, Value }},
{ ocUnion, {{ Reference, Reference }, 0, Reference }},
{ ocVLookup, {{ Value, ReferenceOrForceArray, Value, Value }, 0, Value }},
{ ocVar, {{ Reference }, 1, Value }},
{ ocVarA, {{ Reference }, 1, Value }},
{ ocVarP, {{ Reference }, 1, Value }},
{ ocVarPA, {{ Reference }, 1, Value }},
{ ocVar, {{ ReferenceOrRefArray }, 1, Value }},
{ ocVarA, {{ ReferenceOrRefArray }, 1, Value }},
{ ocVarP, {{ ReferenceOrRefArray }, 1, Value }},
{ ocVarPA, {{ ReferenceOrRefArray }, 1, Value }},
{ ocVarP_MS, {{ Reference }, 1, Value }},
{ ocVarS, {{ Reference }, 1, Value }},
{ ocWorkday_MS, {{ Value, Value, Value, Reference }, 0, Value }},
@@ -613,6 +615,9 @@ void ScParameterClassification::GenerateDocumentation()
case Reference :
aStr.append(" Reference");
break;
case ReferenceOrRefArray :
aStr.append(" ReferenceOrRefArray");
break;
case Array :
aStr.append(" Array");
break;
@@ -664,6 +669,9 @@ void ScParameterClassification::GenerateDocumentation()
case Reference :
aStr.append(" -> Reference");
break;
case ReferenceOrRefArray :
aStr.append(" -> ReferenceOrRefArray");
break;
case Array :
aStr.append(" -> Array");
break;