// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using Debug = System.Diagnostics.Debug; namespace Internal.TypeSystem { /// /// Contains utility functionality for canonicalization used by multiple types. /// public static class StandardCanonicalizationAlgorithm { /// /// Returns a new instantiation that canonicalizes all types in /// if possible under the policy of '' /// /// Instantiation to canonicalize. /// The type of canonicalization to apply. /// True if the returned instantiation is different from ''. public static Instantiation ConvertInstantiationToCanonForm(Instantiation instantiation, CanonicalFormKind kind, out bool changed) { TypeDesc[] canonInstantiation = null; CanonicalFormKind currentKind = kind; CanonicalFormKind startLoopKind; // This logic is wrapped in a loop because we might potentially need to restart canonicalization if the policy // changes due to one of the instantiation arguments already being universally canonical. do { startLoopKind = currentKind; for (int instantiationIndex = 0; instantiationIndex < instantiation.Length; instantiationIndex++) { TypeDesc typeToConvert = instantiation[instantiationIndex]; TypeDesc canonForm = ConvertToCanon(typeToConvert, ref currentKind); if (typeToConvert != canonForm || canonInstantiation != null) { if (canonInstantiation == null) { canonInstantiation = new TypeDesc[instantiation.Length]; for (int i = 0; i < instantiationIndex; i++) canonInstantiation[i] = instantiation[i]; } canonInstantiation[instantiationIndex] = canonForm; } } // Optimization: even if canonical policy changed, we don't actually need to re-run the loop // for instantiations that only have a single element. if (instantiation.Length == 1) { break; } } while (currentKind != startLoopKind); changed = canonInstantiation != null; if (changed) { return new Instantiation(canonInstantiation); } return instantiation; } // Helper API to convert a type to its canonical or universal canonical form. // Note that for now, there is no mixture between specific canonical and universal canonical forms, // meaning that the canonical form or Foo can either be Foo<__Canon, int> or // Foo<__UniversalCanon, __UniversalCanon>. It cannot be Foo<__Canon, __UniversalCanon> (yet) // for simplicity. We can always change that rule in the futue and add support for the mixture, but // for now we are keeping it simple. public static TypeDesc ConvertToCanon(TypeDesc typeToConvert, CanonicalFormKind kind) { // Wrap the call to the version that potentially modifies the parameter. External // callers are not interested in that. return ConvertToCanon(typeToConvert, ref kind); } private static TypeDesc ConvertToCanon(TypeDesc typeToConvert, ref CanonicalFormKind kind) { TypeSystemContext context = typeToConvert.Context; if (kind == CanonicalFormKind.Universal) { return context.UniversalCanonType; } else if (kind == CanonicalFormKind.Specific) { if (typeToConvert == context.UniversalCanonType) { kind = CanonicalFormKind.Universal; return context.UniversalCanonType; } else if (typeToConvert.IsSignatureVariable) { return typeToConvert; } else if (typeToConvert.IsDefType) { if (!typeToConvert.IsValueType) return context.CanonType; else if (typeToConvert.HasInstantiation) { TypeDesc convertedType = typeToConvert.ConvertToCanonForm(CanonicalFormKind.Specific); if (convertedType.IsCanonicalSubtype(CanonicalFormKind.Universal)) { kind = CanonicalFormKind.Universal; return context.UniversalCanonType; } return convertedType; } else return typeToConvert; } else if (typeToConvert.IsArray) { return context.CanonType; } else { TypeDesc convertedType = typeToConvert.ConvertToCanonForm(CanonicalFormKind.Specific); if (convertedType.IsCanonicalSubtype(CanonicalFormKind.Universal)) { kind = CanonicalFormKind.Universal; return context.UniversalCanonType; } return convertedType; } } else { Debug.Assert(false); return null; } } } }