Skip to content

Commit

Permalink
fix Issue 11169 - __traits(isAbstractClass) prematurely sets a class …
Browse files Browse the repository at this point in the history
…to be abstract

__traits(isAbstractClass) should resolve forward references of all class member functions, in order to return stable result.
  • Loading branch information
9rnsr committed Apr 27, 2016
1 parent b96f20b commit 6fd1005
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 12 deletions.
9 changes: 8 additions & 1 deletion src/aggregate.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ enum StructPOD
ISPODfwd, // POD not yet computed
};

enum Abstract
{
ABSfwdref = 0, // whether an abstract class is not yet computed
ABSyes, // is abstract class
ABSno, // is not abstract class
};

FuncDeclaration *hasIdentityOpAssign(AggregateDeclaration *ad, Scope *sc);
FuncDeclaration *buildOpAssign(StructDeclaration *sd, Scope *sc);
bool needOpEquals(StructDeclaration *sd);
Expand Down Expand Up @@ -273,7 +280,7 @@ class ClassDeclaration : public AggregateDeclaration
bool com; // true if this is a COM class (meaning it derives from IUnknown)
bool cpp; // true if this is a C++ interface
bool isscope; // true if this is a scope class
bool isabstract; // true if abstract class
Abstract isabstract; // 0: fwdref, 1: is abstract class, 2: not abstract
int inuse; // to prevent recursive attempts
Baseok baseok; // set the progress of base classes resolving
Objc_ClassDeclaration objc;
Expand Down
59 changes: 52 additions & 7 deletions src/dclass.d
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@ import ddmd.statement;
import ddmd.target;
import ddmd.visitor;

enum Abstract : int
{
ABSfwdref = 0, // whether an abstract class is not yet computed
ABSyes, // is abstract class
ABSno, // is not abstract class
}

alias ABSfwdref = Abstract.ABSfwdref;
alias ABSyes = Abstract.ABSyes;
alias ABSno = Abstract.ABSno;

/***********************************************************
*/
struct BaseClass
Expand Down Expand Up @@ -212,7 +223,7 @@ public:
bool com; // true if this is a COM class (meaning it derives from IUnknown)
bool cpp; // true if this is a C++ interface
bool isscope; // true if this is a scope class
bool isabstract; // true if abstract class
Abstract isabstract;
int inuse; // to prevent recursive attempts
Baseok baseok; // set the progress of base classes resolving

Expand Down Expand Up @@ -464,7 +475,7 @@ public:
if (storage_class & STCscope)
isscope = true;
if (storage_class & STCabstract)
isabstract = true;
isabstract = ABSyes;

userAttribDecl = sc.userAttribDecl;

Expand Down Expand Up @@ -1415,18 +1426,52 @@ public:
*/
final bool isAbstract()
{
if (isabstract)
return true;
if (isabstract != ABSfwdref)
return isabstract == ABSyes;

/* Bugzilla 11169: Resolve forward references to all class member functions,
* and determine whether this class is abstract.
*/
extern (C++) static int func(Dsymbol s, void* param)
{
auto fd = s.isFuncDeclaration();
if (!fd)
return 0;
if (fd.storage_class & STCstatic)
return 0;

if (fd._scope)
fd.semantic(null);

if (fd.isAbstract())
return 1;
return 0;
}

for (size_t i = 0; i < members.dim; i++)
{
auto s = (*members)[i];
if (s.apply(&func, cast(void*)this))
{
isabstract = ABSyes;
return true;
}
}

/* Iterate inherited member functions and check their abstract attribute.
*/
for (size_t i = 1; i < vtbl.dim; i++)
{
FuncDeclaration fd = vtbl[i].isFuncDeclaration();
//printf("\tvtbl[%d] = %p\n", i, fd);
auto fd = vtbl[i].isFuncDeclaration();
//if (fd) printf("\tvtbl[%d] = [%s] %s\n", i, fd.loc.toChars(), fd.toChars());
if (!fd || fd.isAbstract())
{
isabstract = true;
isabstract = ABSyes;
return true;
}
}

isabstract = ABSno;
return false;
}

Expand Down
11 changes: 11 additions & 0 deletions src/dsymbol.d
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,17 @@ public:
return toAlias();
}

/*********************************
* Iterate this dsymbol or members of this scoped dsymbol, then
* call `fp` with the found symbol and `param`.
* Params:
* fp = function pointer to process the iterated symbol.
* If it returns nonzero, the iteration will be aborted.
* param = a parameter passed to fp.
* Returns:
* nonzero if the iteration is aborted by the return value of fp,
* or 0 if it's completed.
*/
int apply(Dsymbol_apply_ft_t fp, void* param)
{
return (*fp)(this, param);
Expand Down
2 changes: 2 additions & 0 deletions src/dtemplate.d
Original file line number Diff line number Diff line change
Expand Up @@ -8700,6 +8700,8 @@ public:

override int apply(Dsymbol_apply_ft_t fp, void* param)
{
if (_scope) // if fwd reference
semantic(null); // try to resolve it
if (members)
{
for (size_t i = 0; i < members.dim; i++)
Expand Down
8 changes: 4 additions & 4 deletions src/func.d
Original file line number Diff line number Diff line change
Expand Up @@ -558,15 +558,15 @@ public:
assert(semanticRun <= PASSsemantic);
semanticRun = PASSsemantic;

parent = sc.parent;
Dsymbol parent = toParent();

if (_scope)
{
sc = _scope;
_scope = null;
}

parent = sc.parent;
Dsymbol parent = toParent();

foverrides.setDim(0); // reset in case semantic() is being retried for this function

storage_class |= sc.stc & ~STCref;
Expand Down Expand Up @@ -884,7 +884,7 @@ public:
}

if (storage_class & STCabstract)
cd.isabstract = true;
cd.isabstract = ABSyes;

// if static function, do not put in vtbl[]
if (!isVirtual())
Expand Down
45 changes: 45 additions & 0 deletions test/compilable/test11169.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// REQUIRED_ARGS: -o-
/*
TEST_OUTPUT:
---
1: false
2: true
3: true
---
*/

class A
{
abstract void foo();
}

template MixinAbstractBar() { abstract void bar(); }

class B1 : A
{
// Use pragma instead of static assert, in order to evaluate
// __traits during ClassDeclaration.semantic().
pragma(msg, "1: ", __traits(isAbstractClass, typeof(this)));
override void foo() {}
}

class B2 : A
{
pragma(msg, "2: ", __traits(isAbstractClass, typeof(this)));
override void foo() {}
abstract void bar();
}

class B3 : A
{
pragma(msg, "3: ", __traits(isAbstractClass, typeof(this)));
override void foo() {}
mixin MixinAbstractBar!();
}

void main()
{
static assert( __traits(compiles, { auto b = new B1(); }));
static assert(!__traits(compiles, { auto b = new B2(); }));
static assert(!__traits(compiles, { auto b = new B3(); }));
}
28 changes: 28 additions & 0 deletions test/fail_compilation/fail11169.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
TEST_OUTPUT:
---
fail_compilation/fail11169.d(16): Error: error evaluating static if expression
---
*/

class A
{
abstract void foo();
}

class B : A
{
// __traits(isAbstractClass) is not usable in static if condition.
static if (__traits(isAbstractClass, typeof(this)))
{
}

override void foo()
{
}
}

void main()
{
B b = new B();
}

0 comments on commit 6fd1005

Please sign in to comment.