internal override void AddDependency(QsQualifiedName identifier)
                {
                    if (this.CurrentNode is null)
                    {
                        throw new ArgumentException("AddDependency requires CurrentNode to be non-null.");
                    }

                    var combination = new TypeResolutionCombination(this.ExprTypeParamResolutions);
                    var typeRes     = combination.CombinedResolutionDictionary.FilterByOrigin(identifier);

                    this.ExprTypeParamResolutions.Clear();

                    var referenceRange = Range.Zero;

                    if (this.CurrentStatementOffset.IsValue &&
                        this.CurrentExpressionRange.IsValue)
                    {
                        referenceRange = this.CurrentStatementOffset.Item + this.CurrentExpressionRange.Item;
                    }

                    var called = new CallGraphNode(identifier);
                    var edge   = new CallGraphEdge(typeRes, referenceRange);

                    this.Graph.AddDependency(this.CurrentNode, called, edge);
                    // If we are not processing all elements, then we need to keep track of what elements
                    // have been processed, and which elements still need to be processed.
                    if (this.WithTrimming &&
                        !this.RequestStack.Contains(called) &&
                        !this.ResolvedNodeSet.Contains(called))
                    {
                        this.RequestStack.Push(called);
                    }
                }
                private (TypedExpression Id, TypedExpression Args)? IsValidScope(QsScope? scope)
                {
                    // if the scope has exactly one statement in it and that statement is a call like expression statement
                    if (scope != null &&
                        scope.Statements.Length == 1 &&
                        scope.Statements[0].Statement is QsStatementKind.QsExpressionStatement expr &&
                        expr.Item.ResolvedType.Resolution.IsUnitType &&
                        expr.Item.Expression is ExpressionKind.CallLikeExpression call &&
                        !TypedExpression.IsPartialApplication(expr.Item.Expression) &&
                        call.Item1.Expression is ExpressionKind.Identifier)
                    {
                        var newCallIdentifier = call.Item1;
                        var callTypeArguments = expr.Item.TypeParameterResolutions;

                        // This relies on anything having type parameters must be a global callable.
                        if (newCallIdentifier.Expression is ExpressionKind.Identifier id &&
                            id.Item1 is Identifier.GlobalCallable global &&
                            callTypeArguments.Any())
                        {
                            // We are dissolving the application of arguments here, so the call's type argument
                            // resolutions have to be moved to the 'identifier' sub expression.
                            var combination           = new TypeResolutionCombination(expr.Item);
                            var combinedTypeArguments = combination.CombinedResolutionDictionary.FilterByOrigin(global.Item);
                            QsCompilerError.Verify(combination.IsValid, "failed to combine type parameter resolution");

                            var globalCallable = this.SharedState.Compilation.Namespaces
                                                 .Where(ns => ns.Name.Equals(global.Item.Namespace))
                                                 .Callables()
                                                 .FirstOrDefault(c => c.FullName.Name.Equals(global.Item.Name));

                            QsCompilerError.Verify(globalCallable != null, $"Could not find the global reference {global.Item}.");

                            var callableTypeParameters = globalCallable.Signature.TypeParameters.Select(param =>
                            {
                                var name = param as QsLocalSymbol.ValidName;
                                QsCompilerError.Verify(!(name is null), "Invalid type parameter name.");
                                return(name);
                            });

                            newCallIdentifier = new TypedExpression(
                                ExpressionKind.NewIdentifier(
                                    id.Item1,
                                    QsNullable <ImmutableArray <ResolvedType> > .NewValue(
                                        callableTypeParameters
                                        .Select(x => combinedTypeArguments[Tuple.Create(global.Item, x.Item)]).ToImmutableArray())),
                                TypedExpression.AsTypeArguments(combinedTypeArguments),
                                call.Item1.ResolvedType,
                                call.Item1.InferredInformation,
                                call.Item1.Range);
                        }

                        return(newCallIdentifier, call.Item2);
                    }

                    return(null);
                }
                internal override void AddDependency(QsQualifiedName identifier)
                {
                    if (this.CurrentNode is null)
                    {
                        throw new ArgumentException("AddDependency requires CurrentNode to be non-null.");
                    }

                    var combination = new TypeResolutionCombination(this.ExprTypeParamResolutions.Append(this.CurrentNode.ParamResolutions));
                    var typeRes     = combination.CombinedResolutionDictionary.FilterByOrigin(identifier);

                    this.ExprTypeParamResolutions.Clear();

                    var referenceRange = Range.Zero;

                    if (this.CurrentStatementOffset.IsValue &&
                        this.CurrentExpressionRange.IsValue)
                    {
                        referenceRange = this.CurrentStatementOffset.Item + this.CurrentExpressionRange.Item;
                    }
                    this.lastReferenceRange = referenceRange;

                    void AddEdge(QsSpecializationKind kind) => this.AddEdge(identifier, kind, typeRes, referenceRange);

                    if (this.IsInCall)
                    {
                        if (this.HasAdjointDependency && this.HasControlledDependency)
                        {
                            AddEdge(QsSpecializationKind.QsControlledAdjoint);
                        }
                        else if (this.HasAdjointDependency)
                        {
                            AddEdge(QsSpecializationKind.QsAdjoint);
                        }
                        else if (this.HasControlledDependency)
                        {
                            AddEdge(QsSpecializationKind.QsControlled);
                        }
                        else
                        {
                            AddEdge(QsSpecializationKind.QsBody);
                        }
                    }
                    else
                    {
                        // The callable is being used in a non-call context, such as being
                        // assigned to a variable or passed as an argument to another callable,
                        // which means it could get a functor applied at some later time.
                        // We're conservative and add all possible kinds defined for the callable.
                        foreach (var kind in this.GetSpecializationKinds(identifier))
                        {
                            AddEdge(kind);
                        }
                    }
                }
                /// <summary>
                /// Handles adding the dependencies for specializations marked as self-inverse.
                /// </summary>
                internal void AddSelfInverseDependency(QsQualifiedName identifier, QsSpecializationKind targetSpec)
                {
                    if (this.CurrentNode is null)
                    {
                        throw new ArgumentException("AddDependency requires CurrentNode to be non-null.");
                    }

                    var combination = new TypeResolutionCombination(new[] { this.CurrentNode.ParamResolutions });
                    var typeRes     = combination.CombinedResolutionDictionary.FilterByOrigin(identifier);

                    this.AddEdge(identifier, targetSpec, typeRes, this.lastReferenceRange);
                }
                public override QsExpressionKind <TypedExpression, Identifier, ResolvedType> OnIdentifier(Identifier sym, QsNullable <ImmutableArray <ResolvedType> > tArgs)
                {
                    if (sym is Identifier.GlobalCallable global)
                    {
                        // We want to skip over intrinsic callables. They will not be monomorphized.
                        if (!this.SharedState.IntrinsicCallableSet.Contains(global.Item))
                        {
                            var combination = new TypeResolutionCombination(this.SharedState.CurrentTypeParamResolutions);
                            this.SharedState.LastCalculatedTypeResolutions = combination.CombinedResolutionDictionary;
                            var typeRes = combination.CombinedResolutionDictionary.FilterByOrigin(global.Item);

                            // Create a new identifier
                            sym   = this.SharedState.GetConcreteIdentifier(global, typeRes);
                            tArgs = QsNullable <ImmutableArray <ResolvedType> > .Null;
                        }
                        this.SharedState.CurrentTypeParamResolutions.Clear();
                    }
                    else if (sym is Identifier.LocalVariable && tArgs.IsValue && tArgs.Item.Any())
                    {
                        throw new ArgumentException($"Local variables cannot have type arguments.");
                    }

                    return(base.OnIdentifier(sym, tArgs));
                }