Skip to content

Commit

Permalink
Avoid use of incorrect instantation argumens when checking for constr…
Browse files Browse the repository at this point in the history
…aint satisfaction (#47258)

- The constraint processing logic in the runtime conflates the idea of constraint checking on open, closed over concrete types, and closed over non-concrete types
- This change adds a tweak to avoid using an instantiation context that isn't related to the type variable being instantiated

Fixes issue #45600 and adds a regression test from the customer.
  • Loading branch information
davidwrighton authored Mar 19, 2021
1 parent cea6fbd commit 76954b4
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 1 deletion.
13 changes: 12 additions & 1 deletion src/coreclr/vm/genmeth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1667,6 +1667,16 @@ BOOL MethodDesc::SatisfiesMethodConstraints(TypeHandle thParent, BOOL fThrowIfNo
SigTypeContext typeContext(this,thParent);
InstantiationContext instContext(&typeContext, NULL);

bool typicalInstMatchesMethodInst = true;
for (DWORD i = 0; i < methodInst.GetNumArgs(); i++)
{
if (typicalInst[i] != methodInst[i])
{
typicalInstMatchesMethodInst = false;
break;
}
}

for (DWORD i = 0; i < methodInst.GetNumArgs(); i++)
{
TypeHandle thArg = methodInst[i];
Expand All @@ -1679,7 +1689,8 @@ BOOL MethodDesc::SatisfiesMethodConstraints(TypeHandle thParent, BOOL fThrowIfNo
tyvar->LoadConstraints(); //TODO: is this necessary for anything but the typical method?

// Pass in the InstatiationContext so contraints can be correctly evaluated
if (!tyvar->SatisfiesConstraints(&typeContext,thArg, &instContext))
// if this is an instantiation where the type variable is in its open position
if (!tyvar->SatisfiesConstraints(&typeContext,thArg, typicalInstMatchesMethodInst ? &instContext : NULL))
{
if (fThrowIfNotSatisfied)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<OutputType>Exe</OutputType>
<CLRTestKind>BuildAndRun</CLRTestKind>
<CLRTestPriority>0</CLRTestPriority>
</PropertyGroup>
<ItemGroup>
<Compile Include="repro.cs" />
</ItemGroup>
</Project>
102 changes: 102 additions & 0 deletions src/tests/Loader/classloader/regressions/GitHub_45600/repro.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;

public abstract class A { }

public class B : A { }

public abstract class C { }

public abstract class C<CTParam> : C where CTParam : A { }

public class D : C<B> { }

public abstract class E
{
internal E() { }

internal abstract Type NamedObjectType { get; }
}

public class E<ETParam> : E
where ETParam : A
{
private readonly F<C<ETParam>> components =
new F<C<ETParam>>();

internal override Type NamedObjectType => typeof(ETParam);

public void Register<ERegMethodParam>()
where ERegMethodParam : C<ETParam>, new()
{
components.Register<ERegMethodParam>();
}
}

public class F<FTParam> where FTParam : class
{
private readonly HashSet<Type> componentTypes = new HashSet<Type>();

private readonly Dictionary<Type, Func<FTParam>> componentFactories =
new Dictionary<Type, Func<FTParam>>();

public void Register<FRegMethodParamHaha>() // F<C<B>>.Register<D>
where FRegMethodParamHaha : class, FTParam, new()
{
}
}

public class G
{
private readonly Dictionary<Type, E> subcontainersByNamedObjectType =
new Dictionary<Type, E>();

private readonly Dictionary<Type, E> subcontainersByRegisteredType =
new Dictionary<Type, E>();

public E<ETParam> RegisterNamedObjectType<ETParam>()
where ETParam : A
{
return RegisterSubcontainer(new E<ETParam>());
}

public GRegMethodParam RegisterSubcontainer<GRegMethodParam>(GRegMethodParam subcontainer)
where GRegMethodParam : E
{
subcontainersByNamedObjectType.Add(subcontainer.NamedObjectType, subcontainer);
subcontainersByRegisteredType.Add(typeof(GRegMethodParam), subcontainer);
return subcontainer;
}

internal void Register<GRegMethodParam1, GRegMethodParam2>()
where GRegMethodParam1 : A
where GRegMethodParam2 : C<GRegMethodParam1>, new()
{
GetSubcontainerFor<GRegMethodParam1>().Register<GRegMethodParam2>(); // E<B>.Reg<D>
}

public E<GGetSebMethodParam> GetSubcontainerFor<GGetSebMethodParam>()
where GGetSebMethodParam : A
{
return (E<GGetSebMethodParam>)GetSubcontainerFor(typeof(GGetSebMethodParam));
}

public E GetSubcontainerFor(Type baseNamedObjectType)
{
return subcontainersByNamedObjectType[baseNamedObjectType];
}
}

class Program
{
static int Main(string[] args)
{
var contaner = new G();
contaner.RegisterNamedObjectType<B>();
contaner.Register<B, D>();
return 100;
}
}

0 comments on commit 76954b4

Please sign in to comment.