e46a49ecf1
Former-commit-id: d0813289fa2d35e1f8ed77530acb4fb1df441bc0
778 lines
24 KiB
C#
778 lines
24 KiB
C#
//
|
|
// pending.cs: Pending method implementation
|
|
//
|
|
// Authors:
|
|
// Miguel de Icaza (miguel@gnu.org)
|
|
// Marek Safar (marek.safar@gmail.com)
|
|
//
|
|
// Dual licensed under the terms of the MIT X11 or GNU GPL
|
|
//
|
|
// Copyright 2001, 2002 Ximian, Inc (http://www.ximian.com)
|
|
// Copyright 2003-2008 Novell, Inc.
|
|
// Copyright 2011 Xamarin Inc
|
|
//
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
#if STATIC
|
|
using IKVM.Reflection;
|
|
using IKVM.Reflection.Emit;
|
|
#else
|
|
using System.Reflection;
|
|
using System.Reflection.Emit;
|
|
#endif
|
|
|
|
namespace Mono.CSharp {
|
|
|
|
struct TypeAndMethods {
|
|
public TypeSpec type;
|
|
public IList<MethodSpec> methods;
|
|
|
|
//
|
|
// Whether it is optional, this is used to allow the explicit/implicit
|
|
// implementation when a base class already implements an interface.
|
|
//
|
|
// For example:
|
|
//
|
|
// class X : IA { } class Y : X, IA { IA.Explicit (); }
|
|
//
|
|
public bool optional;
|
|
|
|
//
|
|
// This flag on the method says `We found a match, but
|
|
// because it was private, we could not use the match
|
|
//
|
|
public MethodData [] found;
|
|
|
|
// If a method is defined here, then we always need to
|
|
// create a proxy for it. This is used when implementing
|
|
// an interface's indexer with a different IndexerName.
|
|
public MethodSpec [] need_proxy;
|
|
}
|
|
|
|
struct ProxyMethodContext : IMemberContext
|
|
{
|
|
readonly TypeContainer container;
|
|
|
|
public ProxyMethodContext (TypeContainer container)
|
|
{
|
|
this.container = container;
|
|
}
|
|
|
|
public TypeSpec CurrentType {
|
|
get {
|
|
throw new NotImplementedException ();
|
|
}
|
|
}
|
|
|
|
public TypeParameters CurrentTypeParameters {
|
|
get {
|
|
throw new NotImplementedException ();
|
|
}
|
|
}
|
|
|
|
public MemberCore CurrentMemberDefinition {
|
|
get {
|
|
throw new NotImplementedException ();
|
|
}
|
|
}
|
|
|
|
public bool IsObsolete {
|
|
get {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public bool IsUnsafe {
|
|
get {
|
|
throw new NotImplementedException ();
|
|
}
|
|
}
|
|
|
|
public bool IsStatic {
|
|
get {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public ModuleContainer Module {
|
|
get {
|
|
return container.Module;
|
|
}
|
|
}
|
|
|
|
public string GetSignatureForError ()
|
|
{
|
|
throw new NotImplementedException ();
|
|
}
|
|
|
|
public ExtensionMethodCandidates LookupExtensionMethod (string name, int arity)
|
|
{
|
|
throw new NotImplementedException ();
|
|
}
|
|
|
|
public FullNamedExpression LookupNamespaceOrType (string name, int arity, LookupMode mode, Location loc)
|
|
{
|
|
throw new NotImplementedException ();
|
|
}
|
|
|
|
public FullNamedExpression LookupNamespaceAlias (string name)
|
|
{
|
|
throw new NotImplementedException ();
|
|
}
|
|
}
|
|
|
|
public class PendingImplementation
|
|
{
|
|
/// <summary>
|
|
/// The container for this PendingImplementation
|
|
/// </summary>
|
|
readonly TypeDefinition container;
|
|
|
|
/// <summary>
|
|
/// This is the array of TypeAndMethods that describes the pending implementations
|
|
/// (both interfaces and abstract methods in base class)
|
|
/// </summary>
|
|
TypeAndMethods [] pending_implementations;
|
|
|
|
PendingImplementation (TypeDefinition container, MissingInterfacesInfo[] missing_ifaces, MethodSpec[] abstract_methods, int total)
|
|
{
|
|
var type_builder = container.Definition;
|
|
|
|
this.container = container;
|
|
pending_implementations = new TypeAndMethods [total];
|
|
|
|
int i = 0;
|
|
if (abstract_methods != null) {
|
|
int count = abstract_methods.Length;
|
|
pending_implementations [i].need_proxy = new MethodSpec [count];
|
|
|
|
pending_implementations [i].methods = abstract_methods;
|
|
pending_implementations [i].found = new MethodData [count];
|
|
pending_implementations [i].type = type_builder;
|
|
++i;
|
|
}
|
|
|
|
foreach (MissingInterfacesInfo missing in missing_ifaces) {
|
|
var iface = missing.Type;
|
|
var mi = MemberCache.GetInterfaceMethods (iface);
|
|
|
|
int count = mi.Count;
|
|
pending_implementations [i].type = iface;
|
|
pending_implementations [i].optional = missing.Optional;
|
|
pending_implementations [i].methods = mi;
|
|
pending_implementations [i].found = new MethodData [count];
|
|
pending_implementations [i].need_proxy = new MethodSpec [count];
|
|
i++;
|
|
}
|
|
}
|
|
|
|
Report Report {
|
|
get {
|
|
return container.Module.Compiler.Report;
|
|
}
|
|
}
|
|
|
|
struct MissingInterfacesInfo {
|
|
public TypeSpec Type;
|
|
public bool Optional;
|
|
|
|
public MissingInterfacesInfo (TypeSpec t)
|
|
{
|
|
Type = t;
|
|
Optional = false;
|
|
}
|
|
}
|
|
|
|
static readonly MissingInterfacesInfo [] EmptyMissingInterfacesInfo = new MissingInterfacesInfo [0];
|
|
|
|
static MissingInterfacesInfo [] GetMissingInterfaces (TypeDefinition container)
|
|
{
|
|
//
|
|
// Interfaces will return all interfaces that the container
|
|
// implements including any inherited interfaces
|
|
//
|
|
var impl = container.Definition.Interfaces;
|
|
|
|
if (impl == null || impl.Count == 0)
|
|
return EmptyMissingInterfacesInfo;
|
|
|
|
var ret = new MissingInterfacesInfo[impl.Count];
|
|
|
|
for (int i = 0; i < ret.Length; i++)
|
|
ret [i] = new MissingInterfacesInfo (impl [i]);
|
|
|
|
// we really should not get here because Object doesnt implement any
|
|
// interfaces. But it could implement something internal, so we have
|
|
// to handle that case.
|
|
if (container.BaseType == null)
|
|
return ret;
|
|
|
|
var base_impls = container.BaseType.Interfaces;
|
|
if (base_impls != null) {
|
|
foreach (TypeSpec t in base_impls) {
|
|
for (int i = 0; i < ret.Length; i++) {
|
|
if (t == ret[i].Type) {
|
|
ret[i].Optional = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
//
|
|
// Factory method: if there are pending implementation methods, we return a PendingImplementation
|
|
// object, otherwise we return null.
|
|
//
|
|
// Register method implementations are either abstract methods
|
|
// flagged as such on the base class or interface methods
|
|
//
|
|
static public PendingImplementation GetPendingImplementations (TypeDefinition container)
|
|
{
|
|
TypeSpec b = container.BaseType;
|
|
|
|
var missing_interfaces = GetMissingInterfaces (container);
|
|
|
|
//
|
|
// If we are implementing an abstract class, and we are not
|
|
// ourselves abstract, and there are abstract methods (C# allows
|
|
// abstract classes that have no abstract methods), then allocate
|
|
// one slot.
|
|
//
|
|
// We also pre-compute the methods.
|
|
//
|
|
bool implementing_abstract = ((b != null) && b.IsAbstract && (container.ModFlags & Modifiers.ABSTRACT) == 0);
|
|
MethodSpec[] abstract_methods = null;
|
|
|
|
if (implementing_abstract){
|
|
var am = MemberCache.GetNotImplementedAbstractMethods (b);
|
|
|
|
if (am == null) {
|
|
implementing_abstract = false;
|
|
} else {
|
|
abstract_methods = new MethodSpec[am.Count];
|
|
am.CopyTo (abstract_methods, 0);
|
|
}
|
|
}
|
|
|
|
int total = missing_interfaces.Length + (implementing_abstract ? 1 : 0);
|
|
if (total == 0)
|
|
return null;
|
|
|
|
var pending = new PendingImplementation (container, missing_interfaces, abstract_methods, total);
|
|
|
|
//
|
|
// check for inherited conflicting methods
|
|
//
|
|
foreach (var p in pending.pending_implementations) {
|
|
//
|
|
// It can happen for generic interfaces only
|
|
//
|
|
if (!p.type.IsGeneric)
|
|
continue;
|
|
|
|
//
|
|
// CLR does not distinguishes between ref and out
|
|
//
|
|
for (int i = 0; i < p.methods.Count; ++i) {
|
|
MethodSpec compared_method = p.methods[i];
|
|
if (compared_method.Parameters.IsEmpty)
|
|
continue;
|
|
|
|
for (int ii = i + 1; ii < p.methods.Count; ++ii) {
|
|
MethodSpec tested_method = p.methods[ii];
|
|
if (compared_method.Name != tested_method.Name)
|
|
continue;
|
|
|
|
if (p.type != tested_method.DeclaringType)
|
|
continue;
|
|
|
|
if (!TypeSpecComparer.Override.IsSame (compared_method.Parameters.Types, tested_method.Parameters.Types))
|
|
continue;
|
|
|
|
bool exact_match = true;
|
|
bool ref_only_difference = false;
|
|
var cp = compared_method.Parameters.FixedParameters;
|
|
var tp = tested_method.Parameters.FixedParameters;
|
|
|
|
for (int pi = 0; pi < cp.Length; ++pi) {
|
|
//
|
|
// First check exact modifiers match
|
|
//
|
|
if ((cp[pi].ModFlags & Parameter.Modifier.RefOutMask) == (tp[pi].ModFlags & Parameter.Modifier.RefOutMask))
|
|
continue;
|
|
|
|
if (((cp[pi].ModFlags | tp[pi].ModFlags) & Parameter.Modifier.RefOutMask) == Parameter.Modifier.RefOutMask) {
|
|
ref_only_difference = true;
|
|
continue;
|
|
}
|
|
|
|
exact_match = false;
|
|
break;
|
|
}
|
|
|
|
if (!exact_match || !ref_only_difference)
|
|
continue;
|
|
|
|
pending.Report.SymbolRelatedToPreviousError (compared_method);
|
|
pending.Report.SymbolRelatedToPreviousError (tested_method);
|
|
pending.Report.Error (767, container.Location,
|
|
"Cannot implement interface `{0}' with the specified type parameters because it causes method `{1}' to differ on parameter modifiers only",
|
|
p.type.GetDefinition().GetSignatureForError (), compared_method.GetSignatureForError ());
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return pending;
|
|
}
|
|
|
|
public enum Operation {
|
|
//
|
|
// If you change this, review the whole InterfaceMethod routine as there
|
|
// are a couple of assumptions on these three states
|
|
//
|
|
Lookup, ClearOne, ClearAll
|
|
}
|
|
|
|
/// <summary>
|
|
/// Whether the specified method is an interface method implementation
|
|
/// </summary>
|
|
public MethodSpec IsInterfaceMethod (MemberName name, TypeSpec ifaceType, MethodData method, out MethodSpec ambiguousCandidate, ref bool optional)
|
|
{
|
|
return InterfaceMethod (name, ifaceType, method, Operation.Lookup, out ambiguousCandidate, ref optional);
|
|
}
|
|
|
|
public void ImplementMethod (MemberName name, TypeSpec ifaceType, MethodData method, bool clear_one, out MethodSpec ambiguousCandidate, ref bool optional)
|
|
{
|
|
InterfaceMethod (name, ifaceType, method, clear_one ? Operation.ClearOne : Operation.ClearAll, out ambiguousCandidate, ref optional);
|
|
}
|
|
|
|
/// <remarks>
|
|
/// If a method in Type `t' (or null to look in all interfaces
|
|
/// and the base abstract class) with name `Name', return type `ret_type' and
|
|
/// arguments `args' implements an interface, this method will
|
|
/// return the MethodInfo that this method implements.
|
|
///
|
|
/// If `name' is null, we operate solely on the method's signature. This is for
|
|
/// instance used when implementing indexers.
|
|
///
|
|
/// The `Operation op' controls whether to lookup, clear the pending bit, or clear
|
|
/// all the methods with the given signature.
|
|
///
|
|
/// The `MethodInfo need_proxy' is used when we're implementing an interface's
|
|
/// indexer in a class. If the new indexer's IndexerName does not match the one
|
|
/// that was used in the interface, then we always need to create a proxy for it.
|
|
///
|
|
/// </remarks>
|
|
public MethodSpec InterfaceMethod (MemberName name, TypeSpec iType, MethodData method, Operation op, out MethodSpec ambiguousCandidate, ref bool optional)
|
|
{
|
|
ambiguousCandidate = null;
|
|
|
|
if (pending_implementations == null)
|
|
return null;
|
|
|
|
TypeSpec ret_type = method.method.ReturnType;
|
|
ParametersCompiled args = method.method.ParameterInfo;
|
|
bool is_indexer = method.method is Indexer.SetIndexerMethod || method.method is Indexer.GetIndexerMethod;
|
|
MethodSpec m;
|
|
|
|
foreach (TypeAndMethods tm in pending_implementations){
|
|
if (!(iType == null || tm.type == iType))
|
|
continue;
|
|
|
|
int method_count = tm.methods.Count;
|
|
for (int i = 0; i < method_count; i++){
|
|
m = tm.methods [i];
|
|
|
|
if (m == null)
|
|
continue;
|
|
|
|
if (is_indexer) {
|
|
if (!m.IsAccessor || m.Parameters.IsEmpty)
|
|
continue;
|
|
} else {
|
|
if (name.Name != m.Name)
|
|
continue;
|
|
|
|
if (m.Arity != name.Arity)
|
|
continue;
|
|
}
|
|
|
|
if (!TypeSpecComparer.Override.IsEqual (m.Parameters, args))
|
|
continue;
|
|
|
|
if (!TypeSpecComparer.Override.IsEqual (m.ReturnType, ret_type)) {
|
|
tm.found[i] = method;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// `need_proxy' is not null when we're implementing an
|
|
// interface indexer and this is Clear(One/All) operation.
|
|
//
|
|
// If `name' is null, then we do a match solely based on the
|
|
// signature and not on the name (this is done in the Lookup
|
|
// for an interface indexer).
|
|
//
|
|
if (op != Operation.Lookup) {
|
|
if (m.IsAccessor != method.method.IsAccessor)
|
|
continue;
|
|
|
|
// If `t != null', then this is an explicitly interface
|
|
// implementation and we can always clear the method.
|
|
// `need_proxy' is not null if we're implementing an
|
|
// interface indexer. In this case, we need to create
|
|
// a proxy if the implementation's IndexerName doesn't
|
|
// match the IndexerName in the interface.
|
|
if (m.DeclaringType.IsInterface && iType == null && name.Name != m.Name) { // TODO: This is very expensive comparison
|
|
tm.need_proxy[i] = method.method.Spec;
|
|
} else {
|
|
tm.methods[i] = null;
|
|
}
|
|
} else {
|
|
tm.found [i] = method;
|
|
optional = tm.optional;
|
|
}
|
|
|
|
if (op == Operation.Lookup && name.ExplicitInterface != null && ambiguousCandidate == null) {
|
|
ambiguousCandidate = m;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Lookups and ClearOne return
|
|
//
|
|
if (op != Operation.ClearAll)
|
|
return m;
|
|
}
|
|
|
|
// If a specific type was requested, we can stop now.
|
|
if (tm.type == iType)
|
|
break;
|
|
}
|
|
|
|
m = ambiguousCandidate;
|
|
ambiguousCandidate = null;
|
|
return m;
|
|
}
|
|
|
|
/// <summary>
|
|
/// C# allows this kind of scenarios:
|
|
/// interface I { void M (); }
|
|
/// class X { public void M (); }
|
|
/// class Y : X, I { }
|
|
///
|
|
/// For that case, we create an explicit implementation function
|
|
/// I.M in Y.
|
|
/// </summary>
|
|
void DefineProxy (TypeSpec iface, MethodSpec base_method, MethodSpec iface_method)
|
|
{
|
|
// TODO: Handle nested iface names
|
|
string proxy_name;
|
|
var ns = iface.MemberDefinition.Namespace;
|
|
if (string.IsNullOrEmpty (ns))
|
|
proxy_name = iface.MemberDefinition.Name + "." + iface_method.Name;
|
|
else
|
|
proxy_name = ns + "." + iface.MemberDefinition.Name + "." + iface_method.Name;
|
|
|
|
var param = iface_method.Parameters;
|
|
|
|
MethodBuilder proxy = container.TypeBuilder.DefineMethod (
|
|
proxy_name,
|
|
MethodAttributes.Private |
|
|
MethodAttributes.HideBySig |
|
|
MethodAttributes.NewSlot |
|
|
MethodAttributes.CheckAccessOnOverride |
|
|
MethodAttributes.Virtual | MethodAttributes.Final,
|
|
CallingConventions.Standard | CallingConventions.HasThis,
|
|
base_method.ReturnType.GetMetaInfo (), param.GetMetaInfo ());
|
|
|
|
if (iface_method.IsGeneric) {
|
|
var gnames = iface_method.GenericDefinition.TypeParameters.Select (l => l.Name).ToArray ();
|
|
proxy.DefineGenericParameters (gnames);
|
|
}
|
|
|
|
for (int i = 0; i < param.Count; i++) {
|
|
string name = param.FixedParameters [i].Name;
|
|
ParameterAttributes attr = ParametersCompiled.GetParameterAttribute (param.FixedParameters [i].ModFlags);
|
|
proxy.DefineParameter (i + 1, attr, name);
|
|
}
|
|
|
|
int top = param.Count;
|
|
var ec = new EmitContext (new ProxyMethodContext (container), proxy.GetILGenerator (), null, null);
|
|
ec.EmitThis ();
|
|
// TODO: GetAllParametersArguments
|
|
for (int i = 0; i < top; i++)
|
|
ec.EmitArgumentLoad (i);
|
|
|
|
ec.Emit (OpCodes.Call, base_method);
|
|
ec.Emit (OpCodes.Ret);
|
|
|
|
container.TypeBuilder.DefineMethodOverride (proxy, (MethodInfo) iface_method.GetMetaInfo ());
|
|
}
|
|
|
|
/// <summary>
|
|
/// This function tells whether one of our base classes implements
|
|
/// the given method (which turns out, it is valid to have an interface
|
|
/// implementation in a base
|
|
/// </summary>
|
|
bool BaseImplements (TypeSpec iface_type, MethodSpec mi, out MethodSpec base_method)
|
|
{
|
|
base_method = null;
|
|
bool base_can_implement = true;
|
|
TypeSpec lookup_type;
|
|
|
|
//
|
|
// Special handling for properties/indexers which cannot have accessors
|
|
// implementing an interface found in different types (e.g. current and base)
|
|
//
|
|
if (mi.IsAccessor && container.Interfaces != null) {
|
|
|
|
bool new_implementation = false;
|
|
foreach (var iface in container.Interfaces) {
|
|
if (TypeSpecComparer.IsEqual (iface, iface_type)) {
|
|
new_implementation = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (new_implementation) {
|
|
MemberFilter filter;
|
|
bool getter = mi.Name [0] == 'g';
|
|
if (mi.Parameters.Count > (getter ? 0 : 1)) {
|
|
var indexer_params = getter ? mi.Parameters : IndexerSpec.CreateParametersFromSetter (mi, mi.Parameters.Count - 1);
|
|
var ptype = getter ? mi.ReturnType : mi.Parameters.Types [mi.Parameters.Count - 1];
|
|
filter = new MemberFilter (MemberCache.IndexerNameAlias, 0, MemberKind.Indexer, indexer_params, ptype);
|
|
} else {
|
|
var pname = mi.Name.Substring (4);
|
|
var ptype = getter ? mi.ReturnType : mi.Parameters.Types [0];
|
|
filter = MemberFilter.Property (pname, ptype);
|
|
}
|
|
|
|
var prop = MemberCache.FindMember (container.CurrentType, filter, BindingRestriction.DeclaredOnly | BindingRestriction.InstanceOnly);
|
|
if (prop != null && (prop.Modifiers & Modifiers.NEW) != 0)
|
|
base_can_implement = false;
|
|
}
|
|
}
|
|
|
|
if (base_can_implement) {
|
|
lookup_type = container.BaseType;
|
|
|
|
if (lookup_type.ImplementsInterface (iface_type, false))
|
|
return true;
|
|
} else {
|
|
lookup_type = container.CurrentType;
|
|
}
|
|
|
|
//
|
|
// Setup filter with no return type to give better error message
|
|
// about mismatch at return type when the check bellow rejects them
|
|
//
|
|
var parameters = mi.Parameters;
|
|
MethodSpec close_match = null;
|
|
|
|
while (true) {
|
|
var candidates = MemberCache.FindMembers (lookup_type, mi.Name, !base_can_implement);
|
|
if (candidates == null) {
|
|
base_method = close_match;
|
|
return false;
|
|
}
|
|
|
|
MethodSpec similar_candidate = null;
|
|
foreach (var candidate in candidates) {
|
|
if (candidate.Kind != MemberKind.Method)
|
|
continue;
|
|
|
|
if (candidate.Arity != mi.Arity)
|
|
continue;
|
|
|
|
var candidate_param = ((MethodSpec) candidate).Parameters;
|
|
if (!TypeSpecComparer.Override.IsEqual (parameters.Types, candidate_param.Types))
|
|
continue;
|
|
|
|
bool modifiers_match = true;
|
|
for (int i = 0; i < parameters.Count; ++i) {
|
|
//
|
|
// First check exact ref/out match
|
|
//
|
|
if ((parameters.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask) == (candidate_param.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask))
|
|
continue;
|
|
|
|
modifiers_match = false;
|
|
|
|
//
|
|
// Different in ref/out only
|
|
//
|
|
if ((parameters.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask) != (candidate_param.FixedParameters[i].ModFlags & Parameter.Modifier.RefOutMask)) {
|
|
if (similar_candidate == null) {
|
|
if (!candidate.IsPublic)
|
|
break;
|
|
|
|
if (!TypeSpecComparer.Override.IsEqual (mi.ReturnType, ((MethodSpec) candidate).ReturnType))
|
|
break;
|
|
|
|
// It's used for ref/out ambiguity overload check
|
|
similar_candidate = (MethodSpec) candidate;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
similar_candidate = null;
|
|
break;
|
|
}
|
|
|
|
if (!modifiers_match)
|
|
continue;
|
|
|
|
//
|
|
// From this point the candidate is used for detailed error reporting
|
|
// because it's very close match to what we are looking for
|
|
//
|
|
var m = (MethodSpec) candidate;
|
|
|
|
if (!m.IsPublic) {
|
|
if (close_match == null)
|
|
close_match = m;
|
|
|
|
continue;
|
|
}
|
|
|
|
if (!TypeSpecComparer.Override.IsEqual (mi.ReturnType, m.ReturnType)) {
|
|
if (close_match == null)
|
|
close_match = m;
|
|
|
|
continue;
|
|
}
|
|
|
|
base_method = m;
|
|
|
|
if (mi.IsGeneric && !Method.CheckImplementingMethodConstraints (container, m, mi)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (base_method != null) {
|
|
if (similar_candidate != null) {
|
|
Report.SymbolRelatedToPreviousError (similar_candidate);
|
|
Report.SymbolRelatedToPreviousError (mi);
|
|
Report.SymbolRelatedToPreviousError (container);
|
|
Report.Warning (1956, 1, ((MemberCore) base_method.MemberDefinition).Location,
|
|
"The interface method `{0}' implementation is ambiguous between following methods: `{1}' and `{2}' in type `{3}'",
|
|
mi.GetSignatureForError (), base_method.GetSignatureForError (), similar_candidate.GetSignatureForError (), container.GetSignatureForError ());
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (!base_can_implement)
|
|
return false;
|
|
|
|
lookup_type = candidates[0].DeclaringType.BaseType;
|
|
if (lookup_type == null) {
|
|
base_method = close_match;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!base_method.IsVirtual) {
|
|
#if STATIC
|
|
var base_builder = base_method.GetMetaInfo () as MethodBuilder;
|
|
if (base_builder != null) {
|
|
//
|
|
// We can avoid creating a proxy if base_method can be marked 'final virtual'. This can
|
|
// be done for all methods from compiled assembly
|
|
//
|
|
base_builder.__SetAttributes (base_builder.Attributes | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.NewSlot);
|
|
return true;
|
|
}
|
|
#endif
|
|
DefineProxy (iface_type, base_method, mi);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifies that any pending abstract methods or interface methods
|
|
/// were implemented.
|
|
/// </summary>
|
|
public bool VerifyPendingMethods ()
|
|
{
|
|
int top = pending_implementations.Length;
|
|
bool errors = false;
|
|
int i;
|
|
|
|
for (i = 0; i < top; i++){
|
|
TypeSpec type = pending_implementations [i].type;
|
|
|
|
for (int j = 0; j < pending_implementations [i].methods.Count; ++j) {
|
|
var mi = pending_implementations[i].methods[j];
|
|
if (mi == null)
|
|
continue;
|
|
|
|
if (type.IsInterface){
|
|
var need_proxy =
|
|
pending_implementations [i].need_proxy [j];
|
|
|
|
if (need_proxy != null) {
|
|
DefineProxy (type, need_proxy, mi);
|
|
continue;
|
|
}
|
|
|
|
MethodSpec candidate;
|
|
if (BaseImplements (type, mi, out candidate))
|
|
continue;
|
|
|
|
if (candidate == null) {
|
|
MethodData md = pending_implementations [i].found [j];
|
|
if (md != null)
|
|
candidate = md.method.Spec;
|
|
}
|
|
|
|
Report.SymbolRelatedToPreviousError (mi);
|
|
if (candidate != null) {
|
|
Report.SymbolRelatedToPreviousError (candidate);
|
|
if (candidate.IsStatic) {
|
|
Report.Error (736, container.Location,
|
|
"`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' is static",
|
|
container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError ());
|
|
} else if ((candidate.Modifiers & Modifiers.PUBLIC) == 0) {
|
|
Report.Error (737, container.Location,
|
|
"`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' is not public",
|
|
container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError ());
|
|
} else if (mi.ReturnType.Kind == MemberKind.ByRef) {
|
|
Report.Error (8152, container.Location,
|
|
"`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' return type `{3}' does not return by reference",
|
|
container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError (),
|
|
candidate.ReturnType.GetSignatureForError ());
|
|
} else {
|
|
Report.Error (738, container.Location,
|
|
"`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' return type `{3}' does not match interface member return type `{4}'",
|
|
container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError (),
|
|
candidate.ReturnType.GetSignatureForError (), mi.ReturnType.GetSignatureForError ());
|
|
}
|
|
} else {
|
|
Report.Error (535, container.Location, "`{0}' does not implement interface member `{1}'",
|
|
container.GetSignatureForError (), mi.GetSignatureForError ());
|
|
}
|
|
} else {
|
|
Report.SymbolRelatedToPreviousError (mi);
|
|
Report.Error (534, container.Location, "`{0}' does not implement inherited abstract member `{1}'",
|
|
container.GetSignatureForError (), mi.GetSignatureForError ());
|
|
}
|
|
errors = true;
|
|
}
|
|
}
|
|
return errors;
|
|
}
|
|
}
|
|
}
|