internal override void GetConcepts(ConceptSearchOptions options, ArrayBuilder <NamedTypeSymbol> concepts, Binder originalBinder, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { var searchContainers = (options & ConceptSearchOptions.SearchContainers) != 0; var searchUsings = (options & ConceptSearchOptions.SearchUsings) != 0; // We need not check to see if the container itself is a possible // concept because, if it is, then it has a parent // container, and the below check works fine. if (searchContainers && _container != null) { GetConceptsInContainer(_container, concepts, originalBinder, ref useSiteDiagnostics, options); } // The above is ok if we just want to get all concepts in // a straight line up the scope from here to the global // namespace, but we also need to pull in imports too. if (!searchUsings) { return; } foreach (var u in GetImports(null).Usings) { // This may cause duplicate concepts, since we could // 'using static'-import a container already traversed in this // binder chain. GetConceptsInContainer(u.NamespaceOrType, concepts, originalBinder, ref useSiteDiagnostics, options); } }
internal override void GetConceptInstances(ConceptSearchOptions options, ArrayBuilder <TypeSymbol> instances, Binder originalBinder, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { foreach (var parameter in _namedType.TypeParameters) { if (parameter.IsConceptWitness) { instances.Add(parameter); } } }
/// <summary> /// Gets all concepts directly declared in a container. /// </summary> /// <param name="container"> /// The container to visit. /// </param> /// <param name="concepts"> /// The instance array to populate. /// </param> /// <param name="originalBinder"> /// The call-site binder. /// </param> /// <param name="useSiteDiagnostics"> /// The set of use-site diagnostics to populate with any errors. /// </param> /// <param name="options"> /// The concept look-up options to use. /// </param> private void GetConceptsInContainer(NamespaceOrTypeSymbol container, ArrayBuilder <NamedTypeSymbol> concepts, Binder originalBinder, ref HashSet <DiagnosticInfo> useSiteDiagnostics, ConceptSearchOptions options) { Debug.Assert(container != null, "container being searched should not be null: this should have been checked earlier"); var useStandaloneInstances = (options & ConceptSearchOptions.AllowStandaloneInstances) != 0; foreach (var member in container.GetTypeMembers()) { if (!originalBinder.IsAccessible(member, ref useSiteDiagnostics, originalBinder.ContainingType)) { continue; } // Concepts can declare sub-concepts, but (for now) we don't // consider them. if (member.IsConcept || (useStandaloneInstances && member.IsStandaloneInstance)) { concepts.Add(member); } } }
/// <summary> /// Gets all concept instances directly declared in a container. /// </summary> /// <param name="container"> /// The container to visit. /// </param> /// <param name="instances"> /// The instance array to populate. /// </param> /// <param name="originalBinder"> /// The call-site binder. /// </param> /// <param name="useSiteDiagnostics"> /// The set of use-site diagnostics to populate with any errors. /// </param> /// <param name="options"> /// The concept look-up options to use. /// </param> private void GetConceptInstancesInContainer(NamespaceOrTypeSymbol container, ArrayBuilder <TypeSymbol> instances, Binder originalBinder, ref HashSet <DiagnosticInfo> useSiteDiagnostics, ConceptSearchOptions options) { Debug.Assert(container != null, "container being searched should not be null: this should have been checked earlier"); foreach (var member in container.GetTypeMembers()) { if (!originalBinder.IsAccessible(member, ref useSiteDiagnostics, originalBinder.ContainingType)) { continue; } // Assuming that instances don't contain sub-instances. if (member.IsInstance) { instances.Add(member); } // We don't usually go into nested instances, but make an // exception for the inline instance struct of a class, if any. // This is because there would be no other way to get to it. else if (!member.IsConcept) { var inline = member.GetInlineInstanceStruct(); if (inline != null) { instances.Add(inline); } } } }
internal virtual void LookupConceptMethodsInSingleBinder(LookupResult result, string name, int arity, ConsList <Symbol> basesBeingResolved, LookupOptions options, Binder originalBinder, bool diagnose, ref HashSet <DiagnosticInfo> useSiteDiagnostics, ConceptSearchOptions coptions) { var conceptBuilder = ArrayBuilder <NamedTypeSymbol> .GetInstance(); GetConcepts(coptions, conceptBuilder, originalBinder, ref useSiteDiagnostics); var concepts = conceptBuilder.ToImmutableAndFree(); foreach (var concept in concepts) { var methodBuilder = ArrayBuilder <MethodSymbol> .GetInstance(); AddConceptMethods(concept, methodBuilder, name, arity, options, coptions); foreach (var method in methodBuilder.ToImmutableAndFree()) { SingleLookupResult resultOfThisMethod = originalBinder.CheckViability(method, arity, options, concept, diagnose, ref useSiteDiagnostics, basesBeingResolved); result.MergeEqual(resultOfThisMethod); } } }
/// <summary> /// Retrieves the list of concepts available in this /// binder's scope. /// </summary> /// <param name="options"> /// The search options to use when retrieving the list. /// </param> /// <param name="concepts"> /// The array builder to populate with concepts. /// </param> /// <param name="originalBinder"> /// The call-site binder. /// </param> /// <param name="useSiteDiagnostics"> /// Diagnostics set at the use-site. /// </param> internal virtual void GetConcepts(ConceptSearchOptions options, ArrayBuilder <NamedTypeSymbol> concepts, Binder originalBinder, ref HashSet <DiagnosticInfo> useSiteDiagnostics) { // By default, binders have no concepts. }
/// <summary> /// Adds the concept extension methods available in a concept to a /// candidate method list. /// </summary> /// <param name="concept"> /// The concept we are searching for CEMs. /// </param> /// <param name="methods"> /// The method array being populated. /// Any found candidate CEMs are added here. /// </param> /// <param name="nameOpt"> /// The name of the method under lookup; may be null. /// </param> /// <param name="arity"> /// The arity of the method under lookup. /// </param> /// <param name="options"> /// The option set being used for this lookup. /// </param> /// <param name="conceptOptions"> /// The concept-level options set being used for this lookup. /// </param> private void AddConceptMethods(NamedTypeSymbol concept, ArrayBuilder <MethodSymbol> methods, string nameOpt, int arity, LookupOptions options, ConceptSearchOptions conceptOptions) { var allowExtensions = (conceptOptions & ConceptSearchOptions.NoConceptExtensions) == 0; var allowNonExtensions = (conceptOptions & ConceptSearchOptions.ConceptExtensionsOnly) == 0; var allowStandaloneInstances = (conceptOptions & ConceptSearchOptions.AllowStandaloneInstances) != 0; Debug.Assert(concept != null, "cannot get methods from null concept"); Debug.Assert(concept.IsConcept || concept.IsStandaloneInstance, $"'{nameof(concept)}' is not a concept or standalone instance"); Debug.Assert(!concept.IsStandaloneInstance || allowStandaloneInstances, $"'{nameof(concept)}' is a standalone instance, which is not allowed here"); Debug.Assert(0 <= arity, "arity cannot be negative"); // This part is mostly copied from DoGetExtensionMethods. var members = nameOpt == null?concept.GetMembersUnordered() : concept.GetSimpleNonTypeMembers(nameOpt); foreach (var member in members) { if (member.Kind != SymbolKind.Method) { continue; } var method = (MethodSymbol)member; var extensionSituationOk = method.IsConceptExtensionMethod ? allowExtensions : allowNonExtensions; if (!extensionSituationOk) { continue; } var arityOk = (options & LookupOptions.AllMethodsOnArityZero) != 0 || arity == method.Arity; if (!arityOk) { continue; } // @MattWindsor91 (Concept-C# 2017) // If we picked up this method from a concept, we need to // infer the specific instance we'll actually be calling. // Also, if the concept or standalone instance had type // parameters, these need to be inferred. var mustInferConceptParams = concept.IsConcept || concept.IsGenericType; // In these cases, `method` will look like // `C<TC>.M<TM>(this TC x, ...)`. // Our prototype solution is to use a synthesised // symbol that looks like // `M<TM, TC, implicit I>(this TC x, ...) // where I : C<TC>` // which pushes all of the issues into the method // type inferrer. When we construct the method with // the inferred arguments, we de-mangle the method back // to normal. // // This is a nice party trick, but should eventually be // done properly: see the commentary in // `GetCandidateConceptExtensionMethods`. var finalMethod = mustInferConceptParams ? (MethodSymbol) new SynthesizedImplicitConceptMethodSymbol(method) : new SynthesizedWitnessMethodSymbol(method, concept); methods.Add(finalMethod); } }