File: ConstantValueSpecialized.cs
Web Access
Project: src\src\Compilers\Core\Portable\Microsoft.CodeAnalysis.csproj (Microsoft.CodeAnalysis)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Diagnostics;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis
{
    internal partial class ConstantValue
    {
        /// <summary>
        /// The IEEE floating-point spec doesn't specify which bit pattern an implementation
        /// is required to use when producing NaN values.  Indeed, the spec does recommend
        /// "diagnostic" information "left to the implementer’s discretion" be placed in the
        /// undefined bits. It is therefore likely that NaNs produced on different platforms
        /// will differ even for the same arithmetic such as 0.0 / 0.0.  To ensure that the
        /// compiler behaves in a deterministic way, we force NaN values to use the
        /// IEEE "canonical" form with the diagnostic bits set to zero and the sign bit set
        /// to one.  Conversion of this value to float produces the corresponding
        /// canonical NaN of the float type (IEEE Std 754-2008 section 6.2.3).
        /// </summary>
        private static readonly double _s_IEEE_canonical_NaN = BitConverter.Int64BitsToDouble(unchecked((long)0xFFF8000000000000UL));
 
        private sealed class ConstantValueBad : ConstantValue
        {
            private ConstantValueBad() { }
 
            public static readonly ConstantValueBad Instance = new ConstantValueBad();
 
            public override ConstantValueTypeDiscriminator Discriminator
            {
                get
                {
                    return ConstantValueTypeDiscriminator.Bad;
                }
            }
 
            internal override SpecialType SpecialType
            {
                get { return SpecialType.None; }
            }
 
            // all instances of this class are singletons
            public override bool Equals(ConstantValue? other)
            {
                return ReferenceEquals(this, other);
            }
 
            public override int GetHashCode()
            {
                return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this);
            }
 
            internal override string GetValueToDisplay()
            {
                return "bad";
            }
            public override string ToString(string? format, IFormatProvider? provider)
            {
                return GetValueToDisplay();
            }
        }
 
        private sealed class ConstantValueNull : ConstantValue
        {
            private ConstantValueNull() { }
 
            public static readonly ConstantValueNull Instance = new ConstantValueNull();
            public static readonly ConstantValueNull Uninitialized = new ConstantValueNull();
 
            public override ConstantValueTypeDiscriminator Discriminator
            {
                get
                {
                    return ConstantValueTypeDiscriminator.Null;
                }
            }
 
            internal override SpecialType SpecialType
            {
                get { return SpecialType.None; }
            }
 
            public override string? StringValue
            {
                get
                {
                    return null;
                }
            }
 
            internal override Rope? RopeValue
            {
                get
                {
                    return null;
                }
            }
 
            // all instances of this class are singletons
            public override bool Equals(ConstantValue? other)
            {
                return ReferenceEquals(this, other);
            }
 
            public override int GetHashCode()
            {
                return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this);
            }
 
            public override bool IsDefaultValue
            {
                get
                {
                    return true;
                }
            }
 
            internal override string GetValueToDisplay()
            {
                return ((object)this == (object)Uninitialized) ? "unset" : "null";
            }
 
            public override string ToString(string? format, IFormatProvider? provider)
            {
                return GetValueToDisplay();
            }
        }
 
        private sealed class ConstantValueString : ConstantValue
        {
            private readonly Rope _value;
            /// <summary>
            /// Some string constant values can have large costs to realize. To compensate, we realize
            /// constant values lazily, and hold onto a weak reference. If the next time we're asked for the constant
            /// value the previous one still exists, we can avoid rerealizing it. But we don't want to root the constant
            /// value if it's not being used.
            /// </summary>
            private WeakReference<string>? _constantValueReference;
 
            public ConstantValueString(string value)
            {
                // we should have just one Null regardless string or object.
                RoslynDebug.Assert(value != null, "null strings should be represented as Null constant.");
                _value = Rope.ForString(value);
            }
 
            public ConstantValueString(Rope value)
            {
                // we should have just one Null regardless string or object.
                RoslynDebug.Assert(value != null, "null strings should be represented as Null constant.");
                _value = value;
            }
 
            public override ConstantValueTypeDiscriminator Discriminator
            {
                get
                {
                    return ConstantValueTypeDiscriminator.String;
                }
            }
 
            internal override SpecialType SpecialType
            {
                get { return SpecialType.System_String; }
            }
 
            public override string StringValue
            {
                get
                {
                    string? constantValue = null;
                    if (_constantValueReference?.TryGetTarget(out constantValue) != true)
                    {
                        // Note: we could end up realizing the constant value multiple times if there's
                        // a race here. Currently, this isn't believed to be an issue, as the assignment
                        // to _constantValueReference is atomic so the worst that will happen is we return
                        // different instances of a string constant.
                        constantValue = _value.ToString();
                        _constantValueReference = new WeakReference<string>(constantValue);
                    }
 
                    Debug.Assert(constantValue != null);
                    return constantValue;
                }
            }
 
            internal override Rope RopeValue
            {
                get
                {
                    return _value;
                }
            }
 
            public override int GetHashCode()
            {
                return Hash.Combine(base.GetHashCode(), _value.GetHashCode());
            }
 
            public override bool Equals(ConstantValue? other)
            {
                return base.Equals(other) && _value.Equals(other.RopeValue);
            }
 
            internal override string GetValueToDisplay()
            {
                return (_value == null) ? "null" : string.Format("\"{0}\"", _value);
            }
 
            public override string ToString(string? format, IFormatProvider? provider)
            {
                int formatLength = RopeValue.Length;
                if (format is not null && int.TryParse(format, out var len))
                {
                    formatLength = len;
                }
 
                return formatLength < RopeValue.Length ?
                    @$"""{RopeValue.ToString(Math.Max(formatLength - 3, 0))}...""" :
                    @$"""{RopeValue}""";
            }
        }
 
        private sealed class ConstantValueDecimal : ConstantValue
        {
            private readonly decimal _value;
 
            public ConstantValueDecimal(decimal value)
            {
                _value = value;
            }
 
            public override ConstantValueTypeDiscriminator Discriminator
            {
                get
                {
                    return ConstantValueTypeDiscriminator.Decimal;
                }
            }
 
            internal override SpecialType SpecialType
            {
                get { return SpecialType.System_Decimal; }
            }
 
            public override decimal DecimalValue
            {
                get
                {
                    return _value;
                }
            }
 
            public override int GetHashCode()
            {
                return Hash.Combine(base.GetHashCode(), _value.GetHashCode());
            }
 
            public override bool Equals(ConstantValue? other)
            {
                return base.Equals(other) && _value == other.DecimalValue;
            }
        }
 
        private sealed class ConstantValueDateTime : ConstantValue
        {
            private readonly DateTime _value;
 
            public ConstantValueDateTime(DateTime value)
            {
                _value = value;
            }
 
            public override ConstantValueTypeDiscriminator Discriminator
            {
                get
                {
                    return ConstantValueTypeDiscriminator.DateTime;
                }
            }
 
            internal override SpecialType SpecialType
            {
                get { return SpecialType.System_DateTime; }
            }
 
            public override DateTime DateTimeValue
            {
                get
                {
                    return _value;
                }
            }
 
            public override int GetHashCode()
            {
                return Hash.Combine(base.GetHashCode(), _value.GetHashCode());
            }
 
            public override bool Equals(ConstantValue? other)
            {
                return base.Equals(other) && _value == other.DateTimeValue;
            }
        }
 
        // base for constant classes that may represent more than one 
        // constant type
        private abstract class ConstantValueDiscriminated : ConstantValue
        {
            private readonly ConstantValueTypeDiscriminator _discriminator;
 
            public ConstantValueDiscriminated(ConstantValueTypeDiscriminator discriminator)
            {
                _discriminator = discriminator;
            }
 
            public override ConstantValueTypeDiscriminator Discriminator
            {
                get
                {
                    return _discriminator;
                }
            }
 
            internal override SpecialType SpecialType
            {
                get { return GetSpecialType(_discriminator); }
            }
        }
 
        // default value of a value type constant. (reference type constants use Null as default)
        private class ConstantValueDefault : ConstantValueDiscriminated
        {
            public static readonly ConstantValueDefault SByte = new ConstantValueDefault(ConstantValueTypeDiscriminator.SByte);
            public static readonly ConstantValueDefault Byte = new ConstantValueDefault(ConstantValueTypeDiscriminator.Byte);
            public static readonly ConstantValueDefault Int16 = new ConstantValueDefault(ConstantValueTypeDiscriminator.Int16);
            public static readonly ConstantValueDefault UInt16 = new ConstantValueDefault(ConstantValueTypeDiscriminator.UInt16);
            public static readonly ConstantValueDefault Int32 = new ConstantValueDefault(ConstantValueTypeDiscriminator.Int32);
            public static readonly ConstantValueDefault UInt32 = new ConstantValueDefault(ConstantValueTypeDiscriminator.UInt32);
            public static readonly ConstantValueDefault Int64 = new ConstantValueDefault(ConstantValueTypeDiscriminator.Int64);
            public static readonly ConstantValueDefault UInt64 = new ConstantValueDefault(ConstantValueTypeDiscriminator.UInt64);
            public static readonly ConstantValueDefault NInt = new ConstantValueDefault(ConstantValueTypeDiscriminator.NInt);
            public static readonly ConstantValueDefault NUInt = new ConstantValueDefault(ConstantValueTypeDiscriminator.NUInt);
            public static readonly ConstantValueDefault Char = new ConstantValueDefault(ConstantValueTypeDiscriminator.Char);
            public static readonly ConstantValueDefault Single = new ConstantValueSingleZero();
            public static readonly ConstantValueDefault Double = new ConstantValueDoubleZero();
            public static readonly ConstantValueDefault Decimal = new ConstantValueDecimalZero();
            public static readonly ConstantValueDefault DateTime = new ConstantValueDefault(ConstantValueTypeDiscriminator.DateTime);
            public static readonly ConstantValueDefault Boolean = new ConstantValueDefault(ConstantValueTypeDiscriminator.Boolean);
 
            protected ConstantValueDefault(ConstantValueTypeDiscriminator discriminator)
                : base(discriminator)
            {
            }
 
            public override byte ByteValue
            {
                get
                {
                    return 0;
                }
            }
 
            public override sbyte SByteValue
            {
                get
                {
                    return 0;
                }
            }
 
            public override bool BooleanValue
            {
                get
                {
                    return false;
                }
            }
 
            public override double DoubleValue
            {
                get
                {
                    return 0;
                }
            }
 
            public override float SingleValue
            {
                get
                {
                    return 0;
                }
            }
 
            public override decimal DecimalValue
            {
                get
                {
                    return 0;
                }
            }
 
            public override char CharValue
            {
                get
                {
                    return default(char);
                }
            }
 
            public override DateTime DateTimeValue
            {
                get
                {
                    return default(DateTime);
                }
            }
 
            // all instances of this class are singletons
            public override bool Equals(ConstantValue? other)
            {
                return ReferenceEquals(this, other);
            }
 
            public override int GetHashCode()
            {
                return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this);
            }
 
            public override bool IsDefaultValue
            {
                get { return true; }
            }
 
            public override string ToString(string? format, IFormatProvider? provider)
            {
                return $"default({GetPrimitiveTypeName()})";
            }
        }
 
        private sealed class ConstantValueDecimalZero : ConstantValueDefault
        {
            internal ConstantValueDecimalZero()
                : base(ConstantValueTypeDiscriminator.Decimal)
            {
            }
 
            public override bool Equals(ConstantValue? other)
            {
                if (ReferenceEquals(other, this))
                {
                    return true;
                }
 
                if (ReferenceEquals(other, null))
                {
                    return false;
                }
 
                return this.Discriminator == other.Discriminator && other.DecimalValue == 0m;
            }
        }
 
        private sealed class ConstantValueDoubleZero : ConstantValueDefault
        {
            internal ConstantValueDoubleZero()
                : base(ConstantValueTypeDiscriminator.Double)
            {
            }
 
            public override bool Equals(ConstantValue? other)
            {
                if (ReferenceEquals(other, this))
                {
                    return true;
                }
 
                if (ReferenceEquals(other, null))
                {
                    return false;
                }
 
                return this.Discriminator == other.Discriminator && other.DoubleValue == 0;
            }
        }
 
        private sealed class ConstantValueSingleZero : ConstantValueDefault
        {
            internal ConstantValueSingleZero()
                : base(ConstantValueTypeDiscriminator.Single)
            {
            }
 
            public override bool Equals(ConstantValue? other)
            {
                if (ReferenceEquals(other, this))
                {
                    return true;
                }
 
                if (ReferenceEquals(other, null))
                {
                    return false;
                }
 
                return this.Discriminator == other.Discriminator && other.SingleValue == 0;
            }
        }
 
        private class ConstantValueOne : ConstantValueDiscriminated
        {
            public static readonly ConstantValueOne SByte = new ConstantValueOne(ConstantValueTypeDiscriminator.SByte);
            public static readonly ConstantValueOne Byte = new ConstantValueOne(ConstantValueTypeDiscriminator.Byte);
            public static readonly ConstantValueOne Int16 = new ConstantValueOne(ConstantValueTypeDiscriminator.Int16);
            public static readonly ConstantValueOne UInt16 = new ConstantValueOne(ConstantValueTypeDiscriminator.UInt16);
            public static readonly ConstantValueOne Int32 = new ConstantValueOne(ConstantValueTypeDiscriminator.Int32);
            public static readonly ConstantValueOne UInt32 = new ConstantValueOne(ConstantValueTypeDiscriminator.UInt32);
            public static readonly ConstantValueOne Int64 = new ConstantValueOne(ConstantValueTypeDiscriminator.Int64);
            public static readonly ConstantValueOne UInt64 = new ConstantValueOne(ConstantValueTypeDiscriminator.UInt64);
            public static readonly ConstantValueOne NInt = new ConstantValueOne(ConstantValueTypeDiscriminator.NInt);
            public static readonly ConstantValueOne NUInt = new ConstantValueOne(ConstantValueTypeDiscriminator.NUInt);
            public static readonly ConstantValueOne Single = new ConstantValueOne(ConstantValueTypeDiscriminator.Single);
            public static readonly ConstantValueOne Double = new ConstantValueOne(ConstantValueTypeDiscriminator.Double);
            public static readonly ConstantValueOne Decimal = new ConstantValueDecimalOne();
            public static readonly ConstantValueOne Boolean = new ConstantValueOne(ConstantValueTypeDiscriminator.Boolean);
            public static readonly ConstantValueOne Char = new ConstantValueOne(ConstantValueTypeDiscriminator.Char);
 
            protected ConstantValueOne(ConstantValueTypeDiscriminator discriminator)
                : base(discriminator)
            {
            }
 
            public override byte ByteValue
            {
                get
                {
                    return 1;
                }
            }
 
            public override sbyte SByteValue
            {
                get
                {
                    return 1;
                }
            }
 
            public override bool BooleanValue
            {
                get
                {
                    return true;
                }
            }
 
            public override double DoubleValue
            {
                get
                {
                    return 1;
                }
            }
 
            public override float SingleValue
            {
                get
                {
                    return 1;
                }
            }
 
            public override decimal DecimalValue
            {
                get
                {
                    return 1;
                }
            }
 
            public override int Int32Value
            {
                get
                {
                    return 1;
                }
            }
 
            public override uint UInt32Value
            {
                get
                {
                    return 1;
                }
            }
 
            public override long Int64Value
            {
                get
                {
                    return 1;
                }
            }
 
            public override ulong UInt64Value
            {
                get
                {
                    return 1;
                }
            }
 
            public override short Int16Value
            {
                get
                {
                    return 1;
                }
            }
 
            public override ushort UInt16Value
            {
                get
                {
                    return 1;
                }
            }
 
            public override char CharValue
            {
                get
                {
                    return (char)1;
                }
            }
 
            public override bool IsOne
            {
                get
                {
                    return true;
                }
            }
 
            // all instances of this class are singletons
            public override bool Equals(ConstantValue? other)
            {
                return ReferenceEquals(this, other);
            }
 
            public override int GetHashCode()
            {
                return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this);
            }
        }
 
        private sealed class ConstantValueDecimalOne : ConstantValueOne
        {
            internal ConstantValueDecimalOne()
                : base(ConstantValueTypeDiscriminator.Decimal)
            {
            }
 
            public override bool Equals(ConstantValue? other)
            {
                if (ReferenceEquals(other, this))
                {
                    return true;
                }
 
                if (ReferenceEquals(other, null))
                {
                    return false;
                }
 
                return this.Discriminator == other.Discriminator && other.DecimalValue == 1m;
            }
        }
 
        private sealed class ConstantValueI8 : ConstantValueDiscriminated
        {
            private readonly byte _value;
 
            public ConstantValueI8(sbyte value)
                : base(ConstantValueTypeDiscriminator.SByte)
            {
                _value = unchecked((byte)value);
            }
 
            public ConstantValueI8(byte value)
                : base(ConstantValueTypeDiscriminator.Byte)
            {
                _value = value;
            }
 
            public override byte ByteValue
            {
                get
                {
                    return _value;
                }
            }
 
            public override sbyte SByteValue
            {
                get
                {
                    return unchecked((sbyte)(_value));
                }
            }
 
            public override int GetHashCode()
            {
                return Hash.Combine(base.GetHashCode(), _value.GetHashCode());
            }
 
            public override bool Equals(ConstantValue? other)
            {
                return base.Equals(other) && _value == other.ByteValue;
            }
        }
 
        private sealed class ConstantValueI16 : ConstantValueDiscriminated
        {
            private readonly short _value;
 
            public ConstantValueI16(short value)
                : base(ConstantValueTypeDiscriminator.Int16)
            {
                _value = value;
            }
 
            public ConstantValueI16(ushort value)
                : base(ConstantValueTypeDiscriminator.UInt16)
            {
                _value = unchecked((short)value);
            }
 
            public ConstantValueI16(char value)
                : base(ConstantValueTypeDiscriminator.Char)
            {
                _value = unchecked((short)value);
            }
 
            public override short Int16Value
            {
                get
                {
                    return _value;
                }
            }
 
            public override ushort UInt16Value
            {
                get
                {
                    return unchecked((ushort)_value);
                }
            }
 
            public override char CharValue
            {
                get
                {
                    return unchecked((char)_value);
                }
            }
 
            public override int GetHashCode()
            {
                return Hash.Combine(base.GetHashCode(), _value.GetHashCode());
            }
 
            public override bool Equals(ConstantValue? other)
            {
                return base.Equals(other) && _value == other.Int16Value;
            }
        }
 
        private sealed class ConstantValueI32 : ConstantValueDiscriminated
        {
            private readonly int _value;
 
            public ConstantValueI32(int value)
                : base(ConstantValueTypeDiscriminator.Int32)
            {
                _value = value;
            }
 
            public ConstantValueI32(uint value)
                : base(ConstantValueTypeDiscriminator.UInt32)
            {
                _value = unchecked((int)value);
            }
 
            public override int Int32Value
            {
                get
                {
                    return _value;
                }
            }
 
            public override uint UInt32Value
            {
                get
                {
                    return unchecked((uint)_value);
                }
            }
 
            public override int GetHashCode()
            {
                return Hash.Combine(base.GetHashCode(), _value.GetHashCode());
            }
 
            public override bool Equals(ConstantValue? other)
            {
                return base.Equals(other) && _value == other.Int32Value;
            }
        }
 
        private sealed class ConstantValueI64 : ConstantValueDiscriminated
        {
            private readonly long _value;
 
            public ConstantValueI64(long value)
                : base(ConstantValueTypeDiscriminator.Int64)
            {
                _value = value;
            }
 
            public ConstantValueI64(ulong value)
                : base(ConstantValueTypeDiscriminator.UInt64)
            {
                _value = unchecked((long)value);
            }
 
            public override long Int64Value
            {
                get
                {
                    return _value;
                }
            }
 
            public override ulong UInt64Value
            {
                get
                {
                    return unchecked((ulong)_value);
                }
            }
 
            public override int GetHashCode()
            {
                return Hash.Combine(base.GetHashCode(), _value.GetHashCode());
            }
 
            public override bool Equals(ConstantValue? other)
            {
                return base.Equals(other) && _value == other.Int64Value;
            }
        }
 
        private sealed class ConstantValueNativeInt : ConstantValueDiscriminated
        {
            // Constants are limited to 32-bit for portability.
            private readonly int _value;
 
            public ConstantValueNativeInt(int value)
                : base(ConstantValueTypeDiscriminator.NInt)
            {
                _value = value;
            }
 
            public ConstantValueNativeInt(uint value)
                : base(ConstantValueTypeDiscriminator.NUInt)
            {
                _value = unchecked((int)value);
            }
 
            public override int Int32Value
            {
                get
                {
                    return _value;
                }
            }
 
            public override uint UInt32Value
            {
                get
                {
                    return unchecked((uint)_value);
                }
            }
 
            public override int GetHashCode()
            {
                return Hash.Combine(base.GetHashCode(), _value.GetHashCode());
            }
 
            public override bool Equals(ConstantValue? other)
            {
                return base.Equals(other) && _value == other.Int32Value;
            }
        }
 
        private sealed class ConstantValueDouble : ConstantValueDiscriminated
        {
            private readonly double _value;
 
            public ConstantValueDouble(double value)
                : base(ConstantValueTypeDiscriminator.Double)
            {
                if (double.IsNaN(value))
                {
                    value = _s_IEEE_canonical_NaN;
                }
 
                _value = value;
            }
 
            public override double DoubleValue
            {
                get
                {
                    return _value;
                }
            }
 
            public override int GetHashCode()
            {
                return Hash.Combine(base.GetHashCode(), _value.GetHashCode());
            }
 
            public override bool Equals(ConstantValue? other)
            {
                return base.Equals(other) && _value.Equals(other.DoubleValue);
            }
 
        }
 
        private sealed class ConstantValueSingle : ConstantValueDiscriminated
        {
            // C# performs constant folding on floating point values in full precision
            // so this class stores values in double precision
            // DoubleValue can be used to get unclipped value
            private readonly double _value;
 
            public ConstantValueSingle(double value)
                : base(ConstantValueTypeDiscriminator.Single)
            {
                if (double.IsNaN(value))
                {
                    value = _s_IEEE_canonical_NaN;
                }
 
                _value = value;
            }
 
            public override double DoubleValue
            {
                get
                {
                    return _value;
                }
            }
 
            public override float SingleValue
            {
                get
                {
                    return (float)_value;
                }
            }
 
            public override int GetHashCode()
            {
                return Hash.Combine(base.GetHashCode(), _value.GetHashCode());
            }
 
            public override bool Equals(ConstantValue? other)
            {
                return base.Equals(other) && _value.Equals(other.DoubleValue);
            }
        }
    }
}