Skip to content

Commit

Permalink
Saving progress. IR translator, removing unnacessary PFxV1Disabled te…
Browse files Browse the repository at this point in the history
…st cases.
  • Loading branch information
anderson-joyle committed Mar 1, 2024
1 parent c0f35b8 commit 0b94653
Show file tree
Hide file tree
Showing 28 changed files with 330 additions and 1,155 deletions.
3 changes: 2 additions & 1 deletion src/libraries/Microsoft.PowerFx.Core/IR/CoercionKind.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ internal enum CoercionKind
CurrencyToText,
TextToCurrency,
CurrencyToBoolean,
BooleanToCurrency,
BooleanToCurrency,
PrimitiveToSingleColumnRecord,
}
}
6 changes: 6 additions & 0 deletions src/libraries/Microsoft.PowerFx.Core/IR/CoercionMatrix.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ public static CoercionKind GetCoercionKind(DType fromType, DType toType, bool us
if (fromType.IsAggregate && toType.Kind == DKind.DataEntity)
{
return CoercionKind.AggregateToDataEntity;
}

// Coercion from a primitive type to a single column record type.
if (fromType.IsPrimitive && toType.IsRecord)
{
return CoercionKind.PrimitiveToSingleColumnRecord;
}

if (toType.IsLargeImage && (fromType.Kind == DKind.Image || fromType == DType.MinimalLargeImage))
Expand Down
3 changes: 3 additions & 0 deletions src/libraries/Microsoft.PowerFx.Core/IR/IRTranslator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1179,6 +1179,9 @@ private IntermediateNode InjectCoercion(IntermediateNode child, IRTranslatorCont
break;
case CoercionKind.PenImageToText:
unaryOpKind = UnaryOpKind.PenImageToText;
break;
case CoercionKind.PrimitiveToSingleColumnRecord:
unaryOpKind = UnaryOpKind.PrimitiveToSingleColumnRecord;
break;
case CoercionKind.UntypedToText:
return new CallNode(IRContext.NotInSource(FormulaType.Build(toType)), BuiltinFunctionsCore.Text_UO, child);
Expand Down
3 changes: 2 additions & 1 deletion src/libraries/Microsoft.PowerFx.Core/IR/Nodes/UnaryOpKind.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ internal enum UnaryOpKind
DateToDateTime,

BooleanToOptionSet,
AggregateToDataEntity,
AggregateToDataEntity,
PrimitiveToSingleColumnRecord,

// Argument pre-processesor in IR Phase.

Expand Down
200 changes: 129 additions & 71 deletions src/libraries/Microsoft.PowerFx.Core/Texl/Builtins/Collect.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,74 @@ protected CollectFunction(string name, TexlStrings.StringGetter description)
return base.GetSignatures(arity);
}

public virtual DType GetCollectedType(PowerFx.Features features, DType argType)
public virtual DType GetCollectedType(Features features, DType argType, TexlNode arg, ref Dictionary<TexlNode, DType> nodeToCoercedTypeMap)
{
Contracts.Assert(argType.IsValid);

return argType;
}

public bool TryGetUnifiedCollectedTypeCanvas(TexlNode[] args, DType[] argTypes, IErrorContainer errors, Features features, out DType collectedType, ref Dictionary<TexlNode, DType> nodeToCoercedTypeMap)
{
Contracts.AssertValue(args);
Contracts.AssertAllValues(args);
Contracts.AssertValue(argTypes);
Contracts.Assert(args.Length == argTypes.Length);
Contracts.AssertValue(errors);
Contracts.Assert(MinArity <= args.Length && args.Length <= MaxArity);

bool fValid = true;
DType itemType = DType.Invalid;

var argc = args.Length;

for (int i = 1; i < argc; i++)
{
DType argType = GetCollectedType(features, argTypes[i], args[i], ref nodeToCoercedTypeMap);

// The subsequent args should all be aggregates.
if (!argType.IsAggregate)
{
errors.EnsureError(args[i], TexlStrings.ErrBadType_Type, argType.GetKindString());
fValid = false;
continue;
}

// Promote the arg type to a table to facilitate unioning.
if (!argType.IsTable)
{
argType = argType.ToTable();
}

if (!itemType.IsValid)
{
itemType = argType;
}
else
{
bool fUnionError = false;
itemType = DType.Union(ref fUnionError, itemType, argType, useLegacyDateTimeAccepts: true, features);
if (fUnionError)
{
errors.EnsureError(DocumentErrorSeverity.Severe, args[i], TexlStrings.ErrIncompatibleTypes);
fValid = false;
}
}

// We only support accessing entities in collections if the collection has only 1 argument that contributes to it's type
if (argc != 2 && itemType.ContainsDataEntityType(DPath.Root))
{
fValid &= DropAllOfKindNested(ref itemType, errors, args[i], DKind.DataEntity);
}
}

Contracts.Assert(!itemType.IsValid || itemType.IsTable);
collectedType = itemType.IsValid ? itemType.ToTable() : DType.EmptyTable;
return fValid;
}

// Attempt to get the unified schema of the items being collected by an invocation.
private bool TryGetUnifiedCollectedType(CheckTypesContext context, TexlNode[] args, DType[] argTypes, IErrorContainer errors, out DType collectedType)
private bool TryGetUnifiedCollectedTypeV1(TexlNode[] args, DType[] argTypes, IErrorContainer errors, Features features, out DType collectedType, ref Dictionary<TexlNode, DType> nodeToCoercedTypeMap)
{
Contracts.AssertValue(args);
Contracts.AssertAllValues(args);
Expand All @@ -117,7 +176,13 @@ private bool TryGetUnifiedCollectedType(CheckTypesContext context, TexlNode[] ar

for (var i = 1; i < argc; i++)
{
DType argType = GetCollectedType(context.Features, argTypes[i]);
DType argType = GetCollectedType(features, argTypes[i], args[i], ref nodeToCoercedTypeMap);

// !!! How is it possible for an argtype to be a primitive and an aggregate at the same time?
//if (argType.DisplayNameProvider == null && argType.Kind == DKind.ObjNull)
//{
// argType.DisplayNameProvider = datasourceType.DisplayNameProvider;
//}

// The subsequent args should all be aggregates.
if (!argType.IsAggregate)
Expand All @@ -128,13 +193,13 @@ private bool TryGetUnifiedCollectedType(CheckTypesContext context, TexlNode[] ar
}

// Promote the arg type to a table to facilitate unioning.
if (!argType.IsRecord)
if (!argType.IsTable)
{
argType = argType.ToRecord();
argType = argType.ToTable();
}

// Checks if all record names exist against table type and if its possible to coerce.
bool checkAggregateNames = argType.CheckAggregateNames(datasourceType, args[i], errors, context.Features, SupportsParamCoercion);
bool checkAggregateNames = argType.CheckAggregateNames(datasourceType, args[i], errors, features, SupportsParamCoercion);
fValid = fValid && checkAggregateNames;

if (!itemType.IsValid)
Expand All @@ -144,7 +209,7 @@ private bool TryGetUnifiedCollectedType(CheckTypesContext context, TexlNode[] ar
else
{
var fUnionError = false;
itemType = DType.Union(ref fUnionError, itemType, argType, useLegacyDateTimeAccepts: true, context.Features);
itemType = DType.Union(ref fUnionError, itemType, argType, useLegacyDateTimeAccepts: true, features);
if (fUnionError)
{
errors.EnsureError(DocumentErrorSeverity.Severe, args[i], TexlStrings.ErrIncompatibleTypes);
Expand All @@ -159,8 +224,8 @@ private bool TryGetUnifiedCollectedType(CheckTypesContext context, TexlNode[] ar
}
}

Contracts.Assert(!itemType.IsValid || itemType.IsRecord);
collectedType = itemType.IsValid ? itemType : DType.EmptyRecord;
Contracts.Assert(!itemType.IsValid || itemType.IsTable);
collectedType = itemType.IsValid ? itemType : DType.EmptyTable;
return fValid;
}

Expand All @@ -182,51 +247,44 @@ public override bool CheckTypes(CheckTypesContext context, TexlNode[] args, DTyp
{
errors.EnsureError(DocumentErrorSeverity.Severe, args[0], TexlStrings.ErrInvalidArgs_Func, Name);
fValid = false;
}
}

DType collectedType = null;

// Get the unified collected type on the RHS. This will generate appropriate
// document errors for invalid arguments such as unsupported aggregate types.
fValid &= TryGetUnifiedCollectedType(context, args, argTypes, errors, out DType collectedType);
Contracts.Assert(collectedType.IsRecord);

if (fValid)
{
if (!collectedType.TryGetCoercionSubType(collectionType, out DType coercionType, out var coercionNeeded, context.Features))
{
fValid = false;
}
else
{
if (coercionNeeded)
{
CollectionUtils.Add(ref nodeToCoercedTypeMap, args[1], coercionType);
}

var fError = false;

returnType = DType.Union(ref fError, collectionType.ToRecord(), collectedType, useLegacyDateTimeAccepts: false, context.Features, allowCoerce: true);

if (fError)
{
fValid = false;
if (!SetErrorForMismatchedColumns(collectionType, collectedType, args[1], errors, context.Features))
{
errors.EnsureError(DocumentErrorSeverity.Severe, args[0], TexlStrings.ErrTableDoesNotAcceptThisType);
}
}
}
}

if (context.Features.PowerFxV1CompatibilityRules && argTypes.Length == 2)
{
if (argTypes[1].IsTable && argTypes[1].Kind != DKind.ObjNull)
{
returnType = returnType.ToTable();
}
}
else
{
returnType = returnType.ToTable();
// document errors for invalid arguments such as unsupported aggregate types.
if (context.Features.PowerFxV1CompatibilityRules)
{
fValid &= TryGetUnifiedCollectedTypeV1(args, argTypes, errors, context.Features, out collectedType, ref nodeToCoercedTypeMap);
}
else
{
fValid &= TryGetUnifiedCollectedTypeCanvas(args, argTypes, errors, context.Features, out collectedType, ref nodeToCoercedTypeMap);
}

Contracts.Assert(collectedType.IsTable);

bool fError = false;
returnType = DType.Union(ref fError, collectionType, collectedType, useLegacyDateTimeAccepts: true, context.Features);
if (fError)
{
fValid = false;
if (!SetErrorForMismatchedColumns(collectionType, collectedType, args[1], errors, context.Features))
{
errors.EnsureError(DocumentErrorSeverity.Severe, args[0], TexlStrings.ErrTableDoesNotAcceptThisType);
}
}

if (fValid)
{
if (context.Features.PowerFxV1CompatibilityRules && argTypes.Length == 2 && (argTypes[1].IsRecord || argTypes[1].IsPrimitive))
{
returnType = returnType.ToRecord();
}
else
{
returnType = returnType.ToTable();
}
}

return fValid;
Expand Down Expand Up @@ -323,8 +381,22 @@ public override bool IsAsyncInvocation(CallNode callNode, TexlBinding binding)
return Arg0RequiresAsync(callNode, binding);
}

public static DType GetCollectedTypeForGivenArgType(Features features, DType argType)
public static DType GetCollectedTypeForGivenArgType(Features features, DType argType, TexlNode arg, ref Dictionary<TexlNode, DType> nodeToCoercedTypeMap)
{
var singleColumnRecordType = GetCollectedTypeForGivenArgType(features, argType);

if (!argType.IsPrimitive)
{
return argType;
}

CollectionUtils.Add(ref nodeToCoercedTypeMap, arg, singleColumnRecordType);

return singleColumnRecordType;
}

public static DType GetCollectedTypeForGivenArgType(Features features, DType argType)
{
Contracts.Assert(argType.IsValid);

if (!argType.IsPrimitive)
Expand All @@ -333,29 +405,15 @@ public static DType GetCollectedTypeForGivenArgType(Features features, DType arg
}

// Passed a scalar; make a record out of it, using a name that depends on the type.
string fieldName = Contracts.VerifyValue(CreateInvariantFieldName(features, argType.Kind));
var fieldName = Contracts.VerifyValue(CreateInvariantFieldName(features, argType.Kind));
return DType.CreateRecord(new TypedName[] { new TypedName(argType, new DName(fieldName)) });
}

protected static string CreateInvariantFieldName(PowerFx.Features features, DKind dKind)
{
Contracts.Assert(dKind >= DKind._Min && dKind < DKind._Lim);

return GetScalarSingleColumnNameForType(features, dKind);
}

private static string GetScalarSingleColumnNameForType(Features features, DKind kind)
{
return kind switch
{
DKind.Image or
DKind.Hyperlink or
DKind.Media or
DKind.Blob or
DKind.PenImage => features.ConsistentOneColumnTableResult ? TableValue.ValueName : "Url",

_ => TableValue.ValueName
};
return MutationUtils.GetScalarSingleColumnNameForType(features, dKind);
}
}

Expand All @@ -373,9 +431,9 @@ public static string GetInvariantNameForRecord(PowerFx.Features features, DKind
return CreateInvariantFieldName(features, dKind);
}

public override DType GetCollectedType(PowerFx.Features features, DType argType)
public override DType GetCollectedType(Features features, DType argType, TexlNode arg, ref Dictionary<TexlNode, DType> nodeToCoercedTypeMap)
{
return GetCollectedTypeForGivenArgType(features, argType);
return GetCollectedTypeForGivenArgType(features, argType, arg, ref nodeToCoercedTypeMap);
}
}
}
2 changes: 1 addition & 1 deletion src/libraries/Microsoft.PowerFx.Core/Types/DType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ public static IEnumerable<DType> GetPrimitiveTypes()
/// Eventually, all display names should come from this centralized source.
/// We should not be using individual DataSource/OptionSet/View references.
/// </summary>
internal DisplayNameProvider DisplayNameProvider { get; private set; }
internal DisplayNameProvider DisplayNameProvider { get; set; }

/// <summary>
/// NamedValueKind is used only for values of kind NamedValue
Expand Down
15 changes: 15 additions & 0 deletions src/libraries/Microsoft.PowerFx.Core/Utils/MutationUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Microsoft.PowerFx.Core.Localization;
using Microsoft.PowerFx.Core.Types;
using Microsoft.PowerFx.Syntax;
using Microsoft.PowerFx.Types;

namespace Microsoft.PowerFx.Core.Utils
{
Expand Down Expand Up @@ -62,6 +63,20 @@ public static void CheckSemantics(TexlBinding binding, TexlFunction function, Te
errors.EnsureError(targetArg, TexlStrings.ErrInvalidArgs_Func, function.Name);
return;
}
}

public static string GetScalarSingleColumnNameForType(Features features, DKind kind)
{
return kind switch
{
DKind.Image or
DKind.Hyperlink or
DKind.Media or
DKind.Blob or
DKind.PenImage => features.ConsistentOneColumnTableResult ? TableValue.ValueName : "Url",

_ => TableValue.ValueName
};
}
}
}
Loading

0 comments on commit 0b94653

Please sign in to comment.