mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-08 20:06:03 +03:00

Notably, the glue layer side of the changed multiple interface inheritance layout (DMD a54e89d) has not been implemented yet. This corresponds to DMD commit 3f6a763c0589dd03c1c206eafd434b593702564e.
308 lines
9.3 KiB
D
308 lines
9.3 KiB
D
// Compiler implementation of the D programming language
|
|
// Copyright (c) 1999-2015 by Digital Mars
|
|
// All Rights Reserved
|
|
// written by Walter Bright
|
|
// http://www.digitalmars.com
|
|
// Distributed under the Boost Software License, Version 1.0.
|
|
// http://www.boost.org/LICENSE_1_0.txt
|
|
|
|
module ddmd.canthrow;
|
|
|
|
import ddmd.aggregate;
|
|
import ddmd.apply;
|
|
import ddmd.arraytypes;
|
|
import ddmd.attrib;
|
|
import ddmd.declaration;
|
|
import ddmd.dstruct;
|
|
import ddmd.dsymbol;
|
|
import ddmd.dtemplate;
|
|
import ddmd.expression;
|
|
import ddmd.func;
|
|
import ddmd.globals;
|
|
import ddmd.init;
|
|
import ddmd.mtype;
|
|
import ddmd.root.rootobject;
|
|
import ddmd.tokens;
|
|
import ddmd.visitor;
|
|
|
|
/********************************************
|
|
* Returns true if the expression may throw exceptions.
|
|
* If 'mustNotThrow' is true, generate an error if it throws
|
|
*/
|
|
extern (C++) bool canThrow(Expression e, FuncDeclaration func, bool mustNotThrow)
|
|
{
|
|
//printf("Expression::canThrow(%d) %s\n", mustNotThrow, toChars());
|
|
// stop walking if we determine this expression can throw
|
|
extern (C++) final class CanThrow : StoppableVisitor
|
|
{
|
|
alias visit = super.visit;
|
|
FuncDeclaration func;
|
|
bool mustNotThrow;
|
|
|
|
public:
|
|
extern (D) this(FuncDeclaration func, bool mustNotThrow)
|
|
{
|
|
this.func = func;
|
|
this.mustNotThrow = mustNotThrow;
|
|
}
|
|
|
|
override void visit(Expression)
|
|
{
|
|
}
|
|
|
|
override void visit(DeclarationExp de)
|
|
{
|
|
stop = Dsymbol_canThrow(de.declaration, func, mustNotThrow);
|
|
}
|
|
|
|
override void visit(CallExp ce)
|
|
{
|
|
if (global.errors && !ce.e1.type)
|
|
return; // error recovery
|
|
/* If calling a function or delegate that is typed as nothrow,
|
|
* then this expression cannot throw.
|
|
* Note that pure functions can throw.
|
|
*/
|
|
Type t = ce.e1.type.toBasetype();
|
|
if (ce.f && ce.f == func)
|
|
return;
|
|
if (t.ty == Tfunction && (cast(TypeFunction)t).isnothrow)
|
|
return;
|
|
if (t.ty == Tdelegate && (cast(TypeFunction)(cast(TypeDelegate)t).next).isnothrow)
|
|
return;
|
|
|
|
if (mustNotThrow)
|
|
{
|
|
if (ce.f)
|
|
{
|
|
ce.error("%s '%s' is not nothrow",
|
|
ce.f.kind(), ce.f.toPrettyChars());
|
|
}
|
|
else
|
|
{
|
|
auto e1 = ce.e1;
|
|
if (e1.op == TOKstar) // print 'fp' if e1 is (*fp)
|
|
e1 = (cast(PtrExp)e1).e1;
|
|
ce.error("'%s' is not nothrow", e1.toChars());
|
|
}
|
|
}
|
|
stop = true;
|
|
}
|
|
|
|
override void visit(NewExp ne)
|
|
{
|
|
if (ne.member)
|
|
{
|
|
if (ne.allocator)
|
|
{
|
|
// Bugzilla 14407
|
|
Type t = ne.allocator.type.toBasetype();
|
|
if (t.ty == Tfunction && !(cast(TypeFunction)t).isnothrow)
|
|
{
|
|
if (mustNotThrow)
|
|
{
|
|
ne.error("%s '%s' is not nothrow",
|
|
ne.allocator.kind(), ne.allocator.toPrettyChars());
|
|
}
|
|
stop = true;
|
|
}
|
|
}
|
|
// See if constructor call can throw
|
|
Type t = ne.member.type.toBasetype();
|
|
if (t.ty == Tfunction && !(cast(TypeFunction)t).isnothrow)
|
|
{
|
|
if (mustNotThrow)
|
|
{
|
|
ne.error("%s '%s' is not nothrow",
|
|
ne.member.kind(), ne.member.toPrettyChars());
|
|
}
|
|
stop = true;
|
|
}
|
|
}
|
|
// regard storage allocation failures as not recoverable
|
|
}
|
|
|
|
override void visit(DeleteExp de)
|
|
{
|
|
Type tb = de.e1.type.toBasetype();
|
|
AggregateDeclaration ad = null;
|
|
switch (tb.ty)
|
|
{
|
|
case Tclass:
|
|
ad = (cast(TypeClass)tb).sym;
|
|
break;
|
|
|
|
case Tpointer:
|
|
tb = (cast(TypePointer)tb).next.toBasetype();
|
|
if (tb.ty == Tstruct)
|
|
ad = (cast(TypeStruct)tb).sym;
|
|
break;
|
|
|
|
case Tarray:
|
|
Type tv = tb.nextOf().baseElemOf();
|
|
if (tv.ty == Tstruct)
|
|
ad = (cast(TypeStruct)tv).sym;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
if (!ad)
|
|
return;
|
|
|
|
if (ad.dtor)
|
|
{
|
|
Type t = ad.dtor.type.toBasetype();
|
|
if (t.ty == Tfunction && !(cast(TypeFunction)t).isnothrow)
|
|
{
|
|
if (mustNotThrow)
|
|
{
|
|
de.error("%s '%s' is not nothrow",
|
|
ad.dtor.kind(), ad.dtor.toPrettyChars());
|
|
}
|
|
stop = true;
|
|
}
|
|
}
|
|
if (ad.aggDelete && tb.ty != Tarray)
|
|
{
|
|
Type t = ad.aggDelete.type;
|
|
if (t.ty == Tfunction && !(cast(TypeFunction)t).isnothrow)
|
|
{
|
|
if (mustNotThrow)
|
|
{
|
|
de.error("%s '%s' is not nothrow",
|
|
ad.aggDelete.kind(), ad.aggDelete.toPrettyChars());
|
|
}
|
|
stop = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
override void visit(AssignExp ae)
|
|
{
|
|
// blit-init cannot throw
|
|
if (ae.op == TOKblit)
|
|
return;
|
|
/* Element-wise assignment could invoke postblits.
|
|
*/
|
|
Type t;
|
|
if (ae.type.toBasetype().ty == Tsarray)
|
|
{
|
|
if (!ae.e2.isLvalue())
|
|
return;
|
|
t = ae.type;
|
|
}
|
|
else if (ae.e1.op == TOKslice)
|
|
t = (cast(SliceExp)ae.e1).e1.type;
|
|
else
|
|
return;
|
|
Type tv = t.baseElemOf();
|
|
if (tv.ty != Tstruct)
|
|
return;
|
|
StructDeclaration sd = (cast(TypeStruct)tv).sym;
|
|
if (!sd.postblit || sd.postblit.type.ty != Tfunction)
|
|
return;
|
|
if ((cast(TypeFunction)sd.postblit.type).isnothrow)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
if (mustNotThrow)
|
|
{
|
|
ae.error("%s '%s' is not nothrow",
|
|
sd.postblit.kind(), sd.postblit.toPrettyChars());
|
|
}
|
|
stop = true;
|
|
}
|
|
}
|
|
|
|
override void visit(NewAnonClassExp)
|
|
{
|
|
assert(0); // should have been lowered by semantic()
|
|
}
|
|
}
|
|
|
|
scope CanThrow ct = new CanThrow(func, mustNotThrow);
|
|
return walkPostorder(e, ct);
|
|
}
|
|
|
|
/**************************************
|
|
* Does symbol, when initialized, throw?
|
|
* Mirrors logic in Dsymbol_toElem().
|
|
*/
|
|
extern (C++) bool Dsymbol_canThrow(Dsymbol s, FuncDeclaration func, bool mustNotThrow)
|
|
{
|
|
AttribDeclaration ad;
|
|
VarDeclaration vd;
|
|
TemplateMixin tm;
|
|
TupleDeclaration td;
|
|
//printf("Dsymbol_toElem() %s\n", s->toChars());
|
|
ad = s.isAttribDeclaration();
|
|
if (ad)
|
|
{
|
|
Dsymbols* decl = ad.include(null, null);
|
|
if (decl && decl.dim)
|
|
{
|
|
for (size_t i = 0; i < decl.dim; i++)
|
|
{
|
|
s = (*decl)[i];
|
|
if (Dsymbol_canThrow(s, func, mustNotThrow))
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
else if ((vd = s.isVarDeclaration()) !is null)
|
|
{
|
|
s = s.toAlias();
|
|
if (s != vd)
|
|
return Dsymbol_canThrow(s, func, mustNotThrow);
|
|
if (vd.storage_class & STCmanifest)
|
|
{
|
|
}
|
|
else if (vd.isStatic() || vd.storage_class & (STCextern | STCtls | STCgshared))
|
|
{
|
|
}
|
|
else
|
|
{
|
|
if (vd._init)
|
|
{
|
|
ExpInitializer ie = vd._init.isExpInitializer();
|
|
if (ie && canThrow(ie.exp, func, mustNotThrow))
|
|
return true;
|
|
}
|
|
if (vd.needsScopeDtor())
|
|
return canThrow(vd.edtor, func, mustNotThrow);
|
|
}
|
|
}
|
|
else if ((tm = s.isTemplateMixin()) !is null)
|
|
{
|
|
//printf("%s\n", tm->toChars());
|
|
if (tm.members)
|
|
{
|
|
for (size_t i = 0; i < tm.members.dim; i++)
|
|
{
|
|
Dsymbol sm = (*tm.members)[i];
|
|
if (Dsymbol_canThrow(sm, func, mustNotThrow))
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
else if ((td = s.isTupleDeclaration()) !is null)
|
|
{
|
|
for (size_t i = 0; i < td.objects.dim; i++)
|
|
{
|
|
RootObject o = (*td.objects)[i];
|
|
if (o.dyncast() == DYNCAST_EXPRESSION)
|
|
{
|
|
Expression eo = cast(Expression)o;
|
|
if (eo.op == TOKdsymbol)
|
|
{
|
|
DsymbolExp se = cast(DsymbolExp)eo;
|
|
if (Dsymbol_canThrow(se.s, func, mustNotThrow))
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|