mirror of https://github.com/mono/CppSharp.git
4 changed files with 338 additions and 0 deletions
@ -0,0 +1,313 @@ |
|||||||
|
using CppSharp.AST; |
||||||
|
|
||||||
|
namespace CppSharp.Passes |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Checks if a pair of types are covariant according to the C++ standard.
|
||||||
|
/// Excerpt from 10.3.7 (Virtual Functions):
|
||||||
|
///
|
||||||
|
/// "The return type of an overriding function shall be either identical
|
||||||
|
/// to the return type of the overridden function or covariant with the
|
||||||
|
/// classes of the functions. If a function D::f overrides a function
|
||||||
|
/// B::f, the return types of the functions are covariant if they satisfy
|
||||||
|
/// the following criteria:
|
||||||
|
///
|
||||||
|
/// - both are pointers to classes, both are lvalue references to classes,
|
||||||
|
/// or both are rvalue references to classes
|
||||||
|
///
|
||||||
|
/// - the class in the return type of B::f is the same class as the class
|
||||||
|
/// in the return type of D::f, or is an unambiguous and accessible
|
||||||
|
/// direct or indirect base class of the class in the return type of D::f
|
||||||
|
///
|
||||||
|
/// - both pointers or references have the same cv-qualification and the
|
||||||
|
/// class type in the return type of D::f has the same cv-qualification
|
||||||
|
/// as or less cv-qualification than the class type in the return type of
|
||||||
|
/// B::f."
|
||||||
|
/// </summary>
|
||||||
|
public class CovariantTypeComparer : ITypeVisitor<bool>, IDeclVisitor<bool> |
||||||
|
{ |
||||||
|
public QualifiedType currentType; |
||||||
|
public Declaration currentDecl; |
||||||
|
|
||||||
|
public CovariantTypeComparer(QualifiedType type) |
||||||
|
{ |
||||||
|
currentType = type; |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitPointerType(PointerType type, |
||||||
|
TypeQualifiers quals) |
||||||
|
{ |
||||||
|
if (!currentType.Qualifiers.Equals(quals)) |
||||||
|
return false; |
||||||
|
|
||||||
|
var currentPointer = currentType.Type as PointerType; |
||||||
|
if (currentPointer == null) |
||||||
|
return false; |
||||||
|
|
||||||
|
currentType = currentPointer.QualifiedPointee; |
||||||
|
var pointee = type.QualifiedPointee; |
||||||
|
|
||||||
|
if (!currentType.Qualifiers.Equals(pointee.Qualifiers)) |
||||||
|
return false; |
||||||
|
|
||||||
|
// Strip any typedefs that might be sugaring the type.
|
||||||
|
currentType = new QualifiedType(currentType.Type.Desugar()); |
||||||
|
var stripPointee = pointee.Type.Desugar(); |
||||||
|
|
||||||
|
return stripPointee.Visit(this); |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitTagType(TagType tag, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
if (!currentType.Qualifiers.Equals(quals)) |
||||||
|
return false; |
||||||
|
|
||||||
|
var tagType = currentType.Type as TagType; |
||||||
|
if (tagType == null) |
||||||
|
return false; |
||||||
|
|
||||||
|
currentDecl = tagType.Declaration; |
||||||
|
return tag.Declaration.Visit(this); |
||||||
|
} |
||||||
|
|
||||||
|
static bool IsDescendentOf(Class @class, Class parent) |
||||||
|
{ |
||||||
|
if (!@class.HasBaseClass) |
||||||
|
return @class == parent; |
||||||
|
|
||||||
|
return IsDescendentOf(@class.BaseClass, parent); |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitClassDecl(Class @class) |
||||||
|
{ |
||||||
|
var currentClass = currentDecl as Class; |
||||||
|
return IsDescendentOf(currentClass, @class); |
||||||
|
} |
||||||
|
|
||||||
|
#region Dummy implementations
|
||||||
|
|
||||||
|
public bool VisitFunctionType(FunctionType function, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitMemberPointerType(MemberPointerType member, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitBuiltinType(BuiltinType builtin, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitTypedefType(TypedefType typedef, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitDecayedType(DecayedType decayed, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitTemplateSpecializationType(TemplateSpecializationType template, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitPrimitiveType(PrimitiveType type, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitDeclaration(Declaration decl, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitTemplateParameterType(TemplateParameterType param, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitTemplateParameterSubstitutionType(TemplateParameterSubstitutionType param, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitInjectedClassNameType(InjectedClassNameType injected, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitDependentNameType(DependentNameType dependent, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitCILType(CILType type, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitArrayType(ArrayType array, TypeQualifiers quals) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitDeclaration(Declaration decl) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitFieldDecl(Field field) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitFunctionDecl(Function function) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitMethodDecl(Method method) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitParameterDecl(Parameter parameter) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitTypedefDecl(TypedefDecl typedef) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitEnumDecl(Enumeration @enum) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitVariableDecl(Variable variable) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitClassTemplateDecl(ClassTemplate template) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitFunctionTemplateDecl(FunctionTemplate template) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitMacroDefinition(MacroDefinition macro) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitNamespace(Namespace @namespace) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitEvent(Event @event) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public bool VisitProperty(Property property) |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
#endregion
|
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This pass checks covariance in virtual override overloads.
|
||||||
|
///
|
||||||
|
/// struct A { virtual A* foo() = 0; };
|
||||||
|
/// struct B : public A { virtual B* foo() override; };
|
||||||
|
///
|
||||||
|
/// The overriden method in B uses a subtype of A, which is not allowed in
|
||||||
|
/// C#, so we need to fix it to use the same type as the overriden method.
|
||||||
|
/// </summary>
|
||||||
|
public class CheckVirtualOverrideReturnCovariance : TranslationUnitPass |
||||||
|
{ |
||||||
|
public override bool VisitMethodDecl(AST.Method method) |
||||||
|
{ |
||||||
|
if (AlreadyVisited(method)) |
||||||
|
return false; |
||||||
|
|
||||||
|
if (!method.IsOverride) |
||||||
|
return false; |
||||||
|
|
||||||
|
var overridenMethod = GetOverridenBaseMethod(method); |
||||||
|
if (overridenMethod == null) |
||||||
|
return false; |
||||||
|
|
||||||
|
// Fix-up any types that are not equal to the overriden base.
|
||||||
|
if (!method.ReturnType.Equals(overridenMethod.ReturnType)) |
||||||
|
{ |
||||||
|
method.ReturnType = overridenMethod.ReturnType; |
||||||
|
|
||||||
|
Driver.Diagnostics.Debug( |
||||||
|
"{0} return type is co-variant with overriden base", |
||||||
|
method.QualifiedOriginalName); |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public Method GetOverridenBaseMethod(Method method) |
||||||
|
{ |
||||||
|
if (!method.IsOverride) |
||||||
|
return null; |
||||||
|
|
||||||
|
var @class = (Class)method.Namespace; |
||||||
|
if (!@class.HasBaseClass) |
||||||
|
return null; |
||||||
|
|
||||||
|
var baseClass = @class.BaseClass; |
||||||
|
var baseMethod = baseClass.Methods.Find(m => IsCompatibleOverload(method, m)); |
||||||
|
|
||||||
|
return baseMethod; |
||||||
|
} |
||||||
|
|
||||||
|
public bool IsCompatibleOverload(Method m1, Method m2) |
||||||
|
{ |
||||||
|
if (m1.Name != m2.Name) |
||||||
|
return false; |
||||||
|
|
||||||
|
if (m1.IsConst != m2.IsConst) |
||||||
|
return false; |
||||||
|
|
||||||
|
if (m1.Parameters.Count != m2.Parameters.Count) |
||||||
|
return false; |
||||||
|
|
||||||
|
for (var i = 0; i < m1.Parameters.Count; ++i) |
||||||
|
{ |
||||||
|
var m1Param = m1.Parameters[i]; |
||||||
|
var m2Param = m2.Parameters[i]; |
||||||
|
|
||||||
|
if (!m1Param.QualifiedType.Equals(m2Param.QualifiedType)) |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
return m1.ReturnType.Equals(m2.ReturnType) |
||||||
|
|| IsCovariantType(m1.ReturnType, m2.ReturnType); |
||||||
|
} |
||||||
|
|
||||||
|
public static bool IsCovariantType(QualifiedType t1, QualifiedType t2) |
||||||
|
{ |
||||||
|
var comparer = new CovariantTypeComparer(t1); |
||||||
|
return t2.Visit(comparer); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue