Repository: shader-slang/slang
File: source/slang/slang-check-conversion.cpp (C++)

Code before:
            if (outCost)
            {
                *outCost = kConversionCost_None;
            }

            return true;
        }
    }

    // nullptr_t can be cast into any pointer type.
    if (as<NullPtrType>(fromType) && as<PtrType>(toType))
    {
        if (outCost)
        {
            *outCost = kConversionCost_NullPtrToPtr;
        }
        if (outToExpr)
        {
            auto* defaultExpr = getASTBuilder()->create<DefaultConstructExpr>();
            defaultExpr->type = QualType(toType);
            *outToExpr = defaultExpr;
        }
        return true;
    }

    // Ptr<T> can be implicitly converted to Ptr<I> if T implements interface I.
    // This is safe because pointers have fixed size regardless of the pointee type.
    if (auto fromPtrType = as<PtrType>(fromType))
    {
        if (auto toPtrType = as<PtrType>(toType))
        {
            auto fromValueType = fromPtrType->getValueType();
            auto toValueType = toPtrType->getValueType();

            // Only allow this conversion if the target pointee type is an interface.
            if (isInterfaceType(toValueType))
            {
                // Check if the source pointee type implements the target interface.
                if (auto witness = tryGetSubtypeWitness(fromValueType, toValueType))
                {
                    if (outCost)
                    {
                        *outCost = kConversionCost_CastToInterface;
                    }
                    if (outToExpr)
                    {
                        // Create a cast expression to convert the pointer type.
                        auto castExpr = getASTBuilder()->create<BuiltinCastExpr>();
                        castExpr->loc = fromExpr->loc;
                        castExpr->type = QualType(toType);
                        castExpr->base = fromExpr;

Review comment (suggestion):
`T*` cannot convert to `IFoo*` because an existential value `IFoo` is a "box" that wraps around its implementations. Basically, if you have T and U implemeting IFoo, then a `IFoo` value by itself is a `Tuple<TypeId, RTTIInfo, union<T,U>>`., and thus have different phyisical layout to `T`. In practice, there is 16 byte header added to the payload section, where the payload section is the union of implementation data.

Code after:
            if (outCost)
            {
                *outCost = kConversionCost_None;
            }

            return true;
        }
    }

    // nullptr_t can be cast into any pointer type.
    if (as<NullPtrType>(fromType) && as<PtrType>(toType))
    {
        if (outCost)
        {
            *outCost = kConversionCost_NullPtrToPtr;
        }
        if (outToExpr)
        {
            auto* defaultExpr = getASTBuilder()->create<DefaultConstructExpr>();
            defaultExpr->type = QualType(toType);
            *outToExpr = defaultExpr;
        }
        return true;
    }
    // none_t can be cast into any Optional<T> type.
    if (as<NoneType>(fromType) && as<OptionalType>(toType))
    {
        if (outCost)
        {
            *outCost = kConversionCost_NoneToOptional;
        }
        if (outToExpr)
        {
            auto resultExpr = getASTBuilder()->create<MakeOptionalExpr>();
            resultExpr->loc = fromExpr->loc;
            resultExpr->type = toType;
            resultExpr->checked = true;
            *outToExpr = resultExpr;
        }
        return true;
    }

    // A enum type can be converted into its underlying tag type.
    if (auto enumDecl = isEnumType(fromType))
    {
        Type* tagType = enumDecl->tagType;
        if (tagType == toType)
        {
            if (outCost)
            {
                *outCost = kConversionCost_RankPromotion;