Skip to content
This repository was archived by the owner on Apr 14, 2022. It is now read-only.

Trying to augment argument set with Evaluator and Expression context #1259

Merged
merged 5 commits into from
Jun 28, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public IMember GetValueFromLambda(LambdaExpression expr) {
public IMember GetValueFromClassCtor(IPythonClassType cls, CallExpression expr) {
SymbolTable.Evaluate(cls.ClassDefinition);
// Determine argument types
var args = ArgumentSet.Empty;
var args = ArgumentSet.Empty(expr, this);
var init = cls.GetMember<IPythonFunctionType>(@"__init__");
if (init != null) {
using (OpenScope(cls.DeclaringModule, cls.ClassDefinition, out _)) {
Expand All @@ -109,7 +109,7 @@ private IMember GetValueFromBound(IPythonBoundType t, CallExpression expr) {
case IPythonFunctionType fn:
return GetValueFromFunctionType(fn, t.Self, expr);
case IPythonPropertyType p:
return GetValueFromProperty(p, t.Self);
return GetValueFromProperty(p, t.Self, expr);
case IPythonIteratorType _ when t.Self is IPythonCollection seq:
return seq.GetIterator();
}
Expand Down Expand Up @@ -202,10 +202,10 @@ public IMember GetValueFromFunctionType(IPythonFunctionType fn, IPythonInstance
return UnknownType;
}

private IMember GetValueFromProperty(IPythonPropertyType p, IPythonInstance instance) {
private IMember GetValueFromProperty(IPythonPropertyType p, IPythonInstance instance, CallExpression expr) {
// Function may not have been walked yet. Do it now.
SymbolTable.Evaluate(p.FunctionDefinition);
return instance.Call(p.Name, ArgumentSet.Empty);
return instance.Call(p.Name, ArgumentSet.Empty(expr, this));
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ private IMember CreateSpecificTypeFromIndex(IGenericType gt, IReadOnlyList<IMemb

// For other types just use supplied arguments
if (indices.Count > 0) {
return gt.CreateSpecificType(new ArgumentSet(indices));
return gt.CreateSpecificType(new ArgumentSet(indices, expr, this));
}
// TODO: report too few type arguments for the generic expression.
return UnknownType;
Expand Down Expand Up @@ -132,7 +132,7 @@ private IMember CreateClassInstance(PythonClassType cls, IReadOnlyList<IMember>

var argSet = initOverload != null
? new ArgumentSet(initFunc, 0, null, callExpr, this)
: new ArgumentSet(constructorArguments);
: new ArgumentSet(constructorArguments, callExpr, this);

argSet.Evaluate();
var specificType = cls.CreateSpecificType(argSet);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ private IMember GetValueFromUnaryOp(UnaryExpression expr, string op) {
var fn = instance.GetPythonType()?.GetMember<IPythonFunctionType>(op);
// Process functions declared in code modules. Scraped/compiled/stub modules do not actually perform any operations.
if (fn?.DeclaringModule != null && (fn.DeclaringModule.ModuleType == ModuleType.User || fn.DeclaringModule.ModuleType == ModuleType.Library)) {
var result = fn.Call(instance, op, ArgumentSet.Empty);
var result = fn.Call(instance, op, ArgumentSet.Empty(expr, this));
if (!result.IsUnknown()) {
return result;
}
Expand Down Expand Up @@ -132,9 +132,9 @@ private IMember GetValueFromBinaryOp(Expression expr) {
if (op.IsComparison()) {
// If the op is a comparison, and the thing on the left is the builtin,
// flip the operation and call it instead.
ret = CallOperator(op.InvertComparison(), right, rightType, left, leftType, tryRight: false);
ret = CallOperator(op.InvertComparison(), right, rightType, left, leftType, expr, tryRight: false);
} else {
ret = CallOperator(op, left, leftType, right, rightType, tryLeft: false);
ret = CallOperator(op, left, leftType, right, rightType, expr, tryLeft: false);
}

if (!ret.IsUnknown()) {
Expand All @@ -146,7 +146,7 @@ private IMember GetValueFromBinaryOp(Expression expr) {

if (rightIsSupported) {
// Try calling the function on the left side, otherwise just return right.
var ret = CallOperator(op, left, leftType, right, rightType, tryRight: false);
var ret = CallOperator(op, left, leftType, right, rightType, expr, tryRight: false);

if (!ret.IsUnknown()) {
return ret;
Expand All @@ -155,13 +155,13 @@ private IMember GetValueFromBinaryOp(Expression expr) {
return op.IsComparison() ? Interpreter.GetBuiltinType(BuiltinTypeId.Bool) : right;
}

var callRet = CallOperator(op, left, leftType, right, rightType);
var callRet = CallOperator(op, left, leftType, right, rightType, expr);
if (!callRet.IsUnknown()) {
return callRet;
}

if (op.IsComparison()) {
callRet = CallOperator(op.InvertComparison(), right, rightType, left, leftType);
callRet = CallOperator(op.InvertComparison(), right, rightType, left, leftType, expr);

if (!callRet.IsUnknown()) {
return callRet;
Expand Down Expand Up @@ -197,18 +197,18 @@ private IMember GetValueFromBinaryOp(Expression expr) {
return left.IsUnknown() ? right : left;
}

private IMember CallOperator(PythonOperator op, IMember left, IPythonType leftType, IMember right, IPythonType rightType, bool tryLeft = true, bool tryRight = true) {
private IMember CallOperator(PythonOperator op, IMember left, IPythonType leftType, IMember right, IPythonType rightType, Expression expr, bool tryLeft = true, bool tryRight = true) {
var (funcName, swappedFuncName) = OpMethodName(op);

if (tryLeft && funcName != null && left is IPythonInstance lpi) {
var ret = leftType.Call(lpi, funcName, new ArgumentSet(new[] { right }));
var ret = leftType.Call(lpi, funcName, new ArgumentSet(new[] { right }, expr, this));
if (!ret.IsUnknown()) {
return ret;
}
}

if (tryRight && swappedFuncName != null && right is IPythonInstance rpi) {
var ret = rightType.Call(rpi, swappedFuncName, new ArgumentSet(new[] { left }));
var ret = rightType.Call(rpi, swappedFuncName, new ArgumentSet(new[] { left }, expr, this));
if (!ret.IsUnknown()) {
return ret;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ private IMember GetValueFromMember(MemberExpression expr) {
case IPythonClassType _:
return value;
case IPythonPropertyType prop:
return prop.Call(instance, prop.Name, ArgumentSet.Empty);
return prop.Call(instance, prop.Name, ArgumentSet.Empty(expr, this));
case IPythonType p:
return new PythonBoundType(p, instance);
case null:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ private void HandleTypedVariable(IPythonType variableType, IMember value, Expres
instance = value;
}
}
instance = instance ?? variableType?.CreateInstance(variableType.Name, ArgumentSet.Empty) ?? Eval.UnknownType;
instance = instance ?? variableType?.CreateInstance(variableType.Name, ArgumentSet.Empty(expr, Eval)) ?? Eval.UnknownType;

if (expr is NameExpression ne) {
Eval.DeclareVariable(ne.Name, instance, VariableSource.Declaration, ne);
Expand Down
3 changes: 1 addition & 2 deletions src/Analysis/Ast/Impl/Analyzer/Symbols/FunctionEvaluator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ internal sealed class FunctionEvaluator : MemberEvaluator {

public FunctionEvaluator(ExpressionEval eval, PythonFunctionOverload overload)
: base(eval, overload.FunctionDefinition) {

_overload = overload;
_function = overload.ClassMember ?? throw new NullReferenceException(nameof(overload.ClassMember));
_self = _function.DeclaringType as PythonClassType;
Expand Down Expand Up @@ -78,7 +77,7 @@ private IPythonType TryDetermineReturnValue() {
if (!annotationType.IsUnknown()) {
// Annotations are typically types while actually functions return
// instances unless specifically annotated to a type such as Type[T].
var t = annotationType.CreateInstance(annotationType.Name, ArgumentSet.Empty);
var t = annotationType.CreateInstance(annotationType.Name, ArgumentSet.WithoutContext);
// If instance could not be created, such as when return type is List[T] and
// type of T is not yet known, just use the type.
var instance = t.IsUnknown() ? annotationType : t;
Expand Down
26 changes: 16 additions & 10 deletions src/Analysis/Ast/Impl/Specializations/Typing/TypingModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,16 +132,7 @@ private void SpecializeMembers() {

_members["Any"] = new AnyType(this);

// AnyStr
var str = Interpreter.GetBuiltinType(BuiltinTypeId.Str);
var bytes = Interpreter.GetBuiltinType(BuiltinTypeId.Bytes);
var unicode = Interpreter.GetBuiltinType(BuiltinTypeId.Unicode);
var anyStrName = new PythonConstant("AnyStr", str);

var anyStrArgs = Interpreter.LanguageVersion.Is3x()
? new IMember[] { anyStrName, str, bytes }
: new IMember[] { anyStrName, str, unicode };
_members["AnyStr"] = GenericTypeParameter.FromTypeVar(new ArgumentSet(anyStrArgs), this);
_members["AnyStr"] = CreateAnyStr();

_members["Optional"] = new GenericType("Optional", CreateOptional, this);
_members["Type"] = new GenericType("Type", CreateType, this);
Expand Down Expand Up @@ -310,6 +301,21 @@ private IPythonType CreateType(IReadOnlyList<IPythonType> typeArgs) {
return Interpreter.UnknownType;
}

private IPythonType CreateAnyStr() {
var str = Interpreter.GetBuiltinType(BuiltinTypeId.Str);
var bytes = Interpreter.GetBuiltinType(BuiltinTypeId.Bytes);
var unicode = Interpreter.GetBuiltinType(BuiltinTypeId.Unicode);
var name = "AnyStr";

var constraints = Interpreter.LanguageVersion.Is3x()
? new IPythonType[] { str, bytes }
: new IPythonType[] { str, unicode };
var docArgs = new[] { $"'{name}'" }.Concat(constraints.Select(c => c.Name));
var documentation = CodeFormatter.FormatSequence("TypeVar", '(', docArgs);

return new GenericTypeParameter(name, this, constraints, documentation, default);
}

private IPythonType CreateGenericClassParameter(IReadOnlyList<IPythonType> typeArgs) {
// Handle Generic[_T1, _T2, ...]. _T1, et al are IGenericTypeParameter from TypeVar.
// Hold the parameter until concrete type is provided at the time of the class instantiation.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public override IMember Next {
} else if (_index < _iteratorType.ItemTypes.Count) {
itemType = _iteratorType.ItemTypes[_index++];
}
return itemType?.CreateInstance(itemType.Name, ArgumentSet.Empty) ?? UnknownType;
return itemType?.CreateInstance(itemType.Name, ArgumentSet.WithoutContext) ?? UnknownType;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@ public override IPythonIterator GetIterator() {
}

public override IMember Index(object index)
=> _collectionType.Index(this, index).GetPythonType().CreateInstance(null, ArgumentSet.Empty);
=> _collectionType.Index(this, index).GetPythonType().CreateInstance(null, ArgumentSet.WithoutContext);
}
}
35 changes: 28 additions & 7 deletions src/Analysis/Ast/Impl/Types/ArgumentSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using Microsoft.Python.Analysis.Analyzer;
using Microsoft.Python.Analysis.Analyzer.Evaluation;
using Microsoft.Python.Analysis.Diagnostics;
Expand All @@ -38,7 +39,7 @@ internal sealed class ArgumentSet : IArgumentSet {
private readonly DictArg _dictArgument;
private bool _evaluated;

public static IArgumentSet Empty = new ArgumentSet();
public static IArgumentSet WithoutContext = new ArgumentSet();

/// <summary>Module that declares the function</summary>
public IPythonModule DeclaringModule { get; }
Expand All @@ -49,19 +50,38 @@ internal sealed class ArgumentSet : IArgumentSet {
public int OverloadIndex { get; }
public IExpressionEvaluator Eval { get; }

public Expression Expression { get; }

private ArgumentSet() { }

public ArgumentSet(IReadOnlyList<IPythonType> typeArgs) {
_arguments = typeArgs.Select(t => new Argument(t)).ToList();
_evaluated = true;
/// <summary>
/// Creates an empty argument set with some context in how the argument set was used.
/// </summary>
/// <param name="expr">Expression associated with argument set.</param>
/// <param name="eval">Evaluator for the expression involving the argument set.</param>
/// <returns></returns>
public static ArgumentSet Empty(Expression expr, IExpressionEvaluator eval) {
return new ArgumentSet(new List<IMember>(), expr, eval);
}

/// <summary>
/// Creates a set of arguments for a call
///
/// Use in the cases a corresponding function is unknown, but it is still convenient to have the context
/// of the expression which the arguments are needed for and the evaluator that is analyzing
/// that expression.
///
/// </summary>
/// <param name="args">Arguments for the call.</param>
/// <param name="expr">Expression for the call.</param>
/// <param name="eval">Evaluator of the current analysis.</param>
public ArgumentSet(IReadOnlyList<IMember> args, Expression expr, IExpressionEvaluator eval) {
_arguments = args.Select(t => new Argument(t)).ToList();
Expression = expr;
Eval = eval;

public ArgumentSet(IReadOnlyList<IMember> memberArgs) {
_arguments = memberArgs.Select(t => new Argument(t)).ToList();
_evaluated = true;
}

public ArgumentSet(IPythonFunctionType fn, int overloadIndex, IPythonInstance instance, CallExpression callExpr, ExpressionEval eval) :
this(fn, overloadIndex, instance, callExpr, eval.Module, eval) { }

Expand All @@ -81,6 +101,7 @@ public ArgumentSet(IPythonFunctionType fn, int overloadIndex, IPythonInstance in
Eval = eval;
OverloadIndex = overloadIndex;
DeclaringModule = fn.DeclaringModule;
Expression = callExpr;

if (callExpr == null) {
// Typically invoked by specialization code without call expression in the code.
Expand Down
7 changes: 6 additions & 1 deletion src/Analysis/Ast/Impl/Types/Definitions/IArgumentSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,13 @@ public interface IArgumentSet {
int OverloadIndex { get; }

/// <summary>
/// Evaluator associated with the set.
/// Evaluator associated with the argument set.
/// </summary>
IExpressionEvaluator Eval { get; }

/// <summary>
/// Expression associated with the argument set
/// </summary>
Expression Expression { get; }
}
}
2 changes: 1 addition & 1 deletion src/Analysis/Ast/Impl/Types/PythonClassType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ public IPythonType CreateSpecificType(IArgumentSet args) {
.Where(p => !p.IsUnknown())
.ToArray();
if (st.Length > 0) {
var type = gt.CreateSpecificType(new ArgumentSet(st));
var type = gt.CreateSpecificType(new ArgumentSet(st, args.Expression, args.Eval));
if (!type.IsUnknown()) {
bases.Add(type);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Analysis/Ast/Impl/Types/PythonFunctionOverload.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ private IMember CreateSpecificReturnFromClassType(IPythonClassType selfClassType
}

if (typeArgs != null) {
var specificReturnValue = returnClassType.CreateSpecificType(new ArgumentSet(typeArgs));
var specificReturnValue = returnClassType.CreateSpecificType(new ArgumentSet(typeArgs, args?.Expression, args?.Eval));
return new PythonInstance(specificReturnValue);
}

Expand Down
4 changes: 2 additions & 2 deletions src/Analysis/Ast/Impl/Types/PythonPropertyType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ public PythonPropertyType(string name, Location location, IPythonType declaringT
public string Description
=> Type == null ? Resources.PropertyOfUnknownType : Resources.PropertyOfType.FormatUI(Type.Name);
public override IMember Call(IPythonInstance instance, string memberName, IArgumentSet args)
=> _getter.Call(args, instance?.GetPythonType() ?? DeclaringType);
=> _getter?.Call(args, instance?.GetPythonType() ?? DeclaringType);
#endregion

internal void AddOverload(IPythonFunctionOverload overload) => _getter = _getter ?? overload;
private IPythonType Type => _getter?.Call(ArgumentSet.Empty, DeclaringType)?.GetPythonType();
private IPythonType Type => _getter?.Call(ArgumentSet.WithoutContext, DeclaringType)?.GetPythonType();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public IReadOnlyList<IPythonCollection> Items
_contents.TryGetValue(key, out var value) ? value : UnknownType;

public override IPythonIterator GetIterator() =>
Call(@"iterkeys", ArgumentSet.Empty) as IPythonIterator ?? new EmptyIterator(Type.DeclaringModule.Interpreter.UnknownType);
Call(@"iterkeys", ArgumentSet.WithoutContext) as IPythonIterator ?? new EmptyIterator(Type.DeclaringModule.Interpreter.UnknownType);

public override IMember Index(object key) => key is IMember m ? this[m] : UnknownType;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public PythonInstanceIterator(IMember instance, IPythonInterpreter interpreter)
__next__ = instance.GetPythonType().GetMember(@"__next__") as IPythonFunctionType;
}

public IMember Next => __next__?.Call(null, @"__next__", ArgumentSet.Empty) ?? UnknownType;
public IMember Next => __next__?.Call(null, @"__next__", ArgumentSet.WithoutContext) ?? UnknownType;

public override IMember Call(string memberName, IArgumentSet args) {
// Specializations
Expand Down
2 changes: 1 addition & 1 deletion src/Analysis/Ast/Impl/Values/PythonInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public virtual IMember Call(string memberName, IArgumentSet args) {
public virtual IPythonIterator GetIterator() {
var iteratorFunc = Type.GetMember(@"__iter__") as IPythonFunctionType;
var o = iteratorFunc?.Overloads.FirstOrDefault();
var instance = o?.Call(ArgumentSet.Empty, Type);
var instance = o?.Call(ArgumentSet.WithoutContext, Type);
if (instance != null) {
return new PythonInstanceIterator(instance, Type.DeclaringModule.Interpreter);
}
Expand Down
Loading