Skip to content

Commit

Permalink
[FIXED] single dispatcher : if an argument is nillable, it can accept…
Browse files Browse the repository at this point in the history
… nil value
  • Loading branch information
arakov committed Jul 24, 2024
1 parent eab3f24 commit 54a7c4c
Show file tree
Hide file tree
Showing 19 changed files with 342 additions and 238 deletions.
3 changes: 2 additions & 1 deletion doc/todo.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ In development:
prom: posting weekly
--------------------------------------
- web server - weather forecast (project setup)
- constructor - single dispatcher; passing nil to single dispatcher (both normal / constructor)
--------------------------------------
- passing nil to single dispatcher (both normal / constructor)
- add support for nilable argument; if the argument is nillable - check for nil and replace with nilValue

=== Iteration 30 ===
--------------------------------------
Expand Down
30 changes: 30 additions & 0 deletions elenasrc3/common/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,36 @@
#include <cstdlib>
#include <cstdint>

#if defined(_MSC_VER)
#define DISABLE_WARNING_PUSH __pragma(warning( push ))
#define DISABLE_WARNING_POP __pragma(warning( pop ))
#define DISABLE_WARNING(warningNumber) __pragma(warning( disable : warningNumber ))

#define DISABLE_WARNING_UNREFERENCED_FORMAL_PARAMETER DISABLE_WARNING(4100)
#define DISABLE_WARNING_UNREFERENCED_FUNCTION DISABLE_WARNING(4505)
#define DISABLE_WARNING_UNINITIALIZED_FIELD DISABLE_WARNING(26495)
// other warnings you want to deactivate...

#elif defined(__GNUC__) || defined(__clang__)
#define DO_PRAGMA(X) _Pragma(#X)
#define DISABLE_WARNING_PUSH DO_PRAGMA(GCC diagnostic push)
#define DISABLE_WARNING_POP DO_PRAGMA(GCC diagnostic pop)
#define DISABLE_WARNING(warningName) DO_PRAGMA(GCC diagnostic ignored #warningName)

#define DISABLE_WARNING_UNREFERENCED_FORMAL_PARAMETER DISABLE_WARNING(-Wunused-parameter)
#define DISABLE_WARNING_UNREFERENCED_FUNCTION DISABLE_WARNING(-Wunused-function)
#define DISABLE_WARNING_UNINITIALIZED_FIELD DISABLE_WARNING(-Wunused-function)
// other warnings you want to deactivate...

#else
#define DISABLE_WARNING_PUSH
#define DISABLE_WARNING_POP
#define DISABLE_WARNING_UNREFERENCED_FORMAL_PARAMETER
#define DISABLE_WARNING_UNREFERENCED_FUNCTION
// other warnings you want to deactivate...

#endif

namespace elena_lang
{

Expand Down
8 changes: 8 additions & 0 deletions elenasrc3/common/lists.h
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,8 @@ namespace elena_lang

int count_int() const { return (int)_list.count(); }

short count_short() const { return (short)_list.count(); }

Iterator start()
{
return _list.start();
Expand Down Expand Up @@ -3201,11 +3203,17 @@ namespace elena_lang
_allocatedSize = _length = 0;
}

DISABLE_WARNING_PUSH
DISABLE_WARNING_UNINITIALIZED_FIELD

CachedList()
{
_allocated = nullptr;
_allocatedSize = _length = 0;
}

DISABLE_WARNING_POP

~CachedList()
{
freeobj(_allocated);
Expand Down
23 changes: 21 additions & 2 deletions elenasrc3/elc/clicommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -764,8 +764,27 @@ enum class VirtualType : int
AbstractEmbeddableWrapper
};

typedef Pair<mssg_t, VirtualType, 0, VirtualType::None> VirtualMethod;
typedef List<VirtualMethod> VirtualMethodList;
struct VirtualMethod
{
mssg_t message;
VirtualType type;
int nillableArgs;

VirtualMethod()
{
message = 0;
type = VirtualType::None;
nillableArgs = 0;
}
VirtualMethod(mssg_t message, VirtualType type, int nillableArgs)
{
this->message = message;
this->type = type;
this->nillableArgs = nillableArgs;
}
};

typedef List<VirtualMethod> VirtualMethodList;

}

Expand Down
2 changes: 1 addition & 1 deletion elenasrc3/elc/cliconst.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

namespace elena_lang
{
#define ELC_REVISION_NUMBER 0x0028
#define ELC_REVISION_NUMBER 0x0029

#if defined _M_IX86 || _M_X64

Expand Down
78 changes: 54 additions & 24 deletions elenasrc3/elc/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2597,6 +2597,8 @@ void Compiler :: generateMethodAttributes(ClassScope& scope, SyntaxNode node,
if (byRefMethod)
methodInfo.byRefHandler = byRefMethod;

methodInfo.nillableArgs = node.findChild(SyntaxKey::NillableInfo).arg.reference;

// check duplicates with different visibility scope
if (MethodScope::checkHint(methodInfo, MethodHint::Private)) {
checkMethodDuplicates(scope, node, message, message & ~STATIC_MESSAGE, false, false);
Expand Down Expand Up @@ -2770,8 +2772,8 @@ inline mssg_t retrieveMethod(VirtualMethodList& implicitMultimethods, mssg_t mul
{
return implicitMultimethods.retrieve<mssg_t>(multiMethod, [](mssg_t arg, VirtualMethod current)
{
return current.value1 == arg;
}).value1;
return current.message == arg;
}).message;
}

mssg_t Compiler :: defineMultimethod(Scope& scope, mssg_t messageRef, bool extensionMode)
Expand Down Expand Up @@ -2845,7 +2847,7 @@ inline TypeInfo mapOutputType(MethodInfo info)
}

void Compiler :: injectVirtualMultimethod(SyntaxNode classNode, SyntaxKey methodType, Scope& scope,
ref_t targetRef, ClassInfo& info, mssg_t multiMethod)
ref_t targetRef, ClassInfo& info, mssg_t multiMethod, int nillableArgs)
{
MethodInfo methodInfo = {};

Expand All @@ -2872,7 +2874,7 @@ void Compiler :: injectVirtualMultimethod(SyntaxNode classNode, SyntaxKey method
inherited = false;

injectVirtualMultimethod(classNode, methodType, scope, targetRef, info, multiMethod, inherited,
mapOutputType(methodInfo), visibility);
mapOutputType(methodInfo), visibility, nillableArgs);

// COMPILER MAGIC : injecting try-multi-method dispather
if (_logic->isTryDispatchAllowed(*scope.moduleScope, multiMethod)) {
Expand All @@ -2890,15 +2892,15 @@ void Compiler :: injectVirtualMethods(SyntaxNode classNode, SyntaxKey methodType
// generate implicit mutli methods
for (auto it = implicitMultimethods.start(); !it.eof(); ++it) {
auto methodInfo = *it;
switch (methodInfo.value2) {
switch (methodInfo.type) {
case VirtualType::Multimethod:
injectVirtualMultimethod(classNode, methodType, scope, targetRef, info, methodInfo.value1);
injectVirtualMultimethod(classNode, methodType, scope, targetRef, info, methodInfo.message, methodInfo.nillableArgs);
break;
case VirtualType::EmbeddableWrapper:
injectVirtualEmbeddableWrapper(classNode, methodType, targetRef, info, methodInfo.value1, false);
injectVirtualEmbeddableWrapper(classNode, methodType, targetRef, info, methodInfo.message, false);
break;
case VirtualType::AbstractEmbeddableWrapper:
injectVirtualEmbeddableWrapper(classNode, methodType, targetRef, info, methodInfo.value1, true);
injectVirtualEmbeddableWrapper(classNode, methodType, targetRef, info, methodInfo.message, true);
break;
default:
break;
Expand Down Expand Up @@ -2996,7 +2998,7 @@ void Compiler :: generateMethodDeclarations(ClassScope& scope, SyntaxNode node,
current.appendChild(SyntaxKey::Multimethod, multiMethod);

if (retrieveMethod(implicitMultimethods, multiMethod) == 0) {
implicitMultimethods.add({ multiMethod, VirtualType::Multimethod });
implicitMultimethods.add({ multiMethod, VirtualType::Multimethod, current.findChild(SyntaxKey::NillableInfo).arg.value});
thirdPassRequired = true;
}
}
Expand All @@ -3016,9 +3018,9 @@ void Compiler :: generateMethodDeclarations(ClassScope& scope, SyntaxNode node,
&& retrieveMethod(implicitMultimethods, byRefMethod) == 0)
{
if (SyntaxTree::ifChildExists(current, SyntaxKey::Attribute, V_ABSTRACT)) {
implicitMultimethods.add({ byRefMethod, VirtualType::AbstractEmbeddableWrapper });
implicitMultimethods.add({ byRefMethod, VirtualType::AbstractEmbeddableWrapper, 0 });
}
else implicitMultimethods.add({ byRefMethod, VirtualType::EmbeddableWrapper });
else implicitMultimethods.add({ byRefMethod, VirtualType::EmbeddableWrapper, 0 });
thirdPassRequired = true;
}
}
Expand Down Expand Up @@ -3577,7 +3579,7 @@ void Compiler :: declareMethodMetaInfo(MethodScope& scope, SyntaxNode node)

void Compiler :: declareParameter(MethodScope& scope, SyntaxNode current, bool withoutWeakMessages,
bool declarationMode, bool& variadicMode, bool& weakSignature, bool& noSignature,
pos_t& paramCount, ref_t* signature, size_t& signatureLen)
pos_t& paramCount, ref_t* signature, size_t& signatureLen, bool& nillable)
{
int index = 1 + scope.parameters.count();

Expand Down Expand Up @@ -3624,6 +3626,8 @@ void Compiler :: declareParameter(MethodScope& scope, SyntaxNode current, bool w

scope.parameters.add(terminal, Parameter(index, paramTypeInfo, sizeInfo.size,
paramTypeInfo.typeRef == V_OUTWRAPPER));

nillable |= paramTypeInfo.nillable;
}

void Compiler :: declareVMTMessage(MethodScope& scope, SyntaxNode node, bool withoutWeakMessages, bool declarationMode)
Expand Down Expand Up @@ -3666,21 +3670,33 @@ void Compiler :: declareVMTMessage(MethodScope& scope, SyntaxNode node, bool wit
flags |= FUNCTION_MESSAGE;
}

int nillableArgs = 0;
int argMask = 1;
bool mixinFunction = false;
bool noSignature = true; // NOTE : is similar to weakSignature except if withoutWeakMessages=true
// if method has an argument list
SyntaxNode current = node.findChild(SyntaxKey::Parameter);
while (current != SyntaxKey::None) {
if (current == SyntaxKey::Parameter) {
bool nillable = false;
declareParameter(scope, current, withoutWeakMessages,
declarationMode, variadicMode, weakSignature, noSignature,
paramCount, signature, signatureLen);
paramCount, signature, signatureLen, nillable);

if (nillable)
{
nillableArgs |= argMask;
}
}
else break;

current = current.nextNode();
argMask <<= 1;
}

if (nillableArgs)
scope.info.nillableArgs = nillableArgs;

// if the signature consists only of generic parameters - ignore it
if (weakSignature)
signatureLen = 0;
Expand Down Expand Up @@ -3855,9 +3871,10 @@ ref_t Compiler :: declareClosureParameters(MethodScope& methodScope, SyntaxNode
ref_t signatures[ARG_COUNT];
size_t signatureLen = 0;
while (argNode == SyntaxKey::Parameter) {
bool dummy = false;
declareParameter(methodScope, argNode, false, false,
variadicMode, weakSingature, noSignature,
paramCount, signatures, signatureLen);
paramCount, signatures, signatureLen, dummy);

if (variadicMode)
flags |= VARIADIC_MESSAGE;
Expand Down Expand Up @@ -3917,6 +3934,9 @@ void Compiler :: declareMethod(MethodScope& methodScope, SyntaxNode node, bool a
addTypeInfo(methodScope, node, SyntaxKey::OutputInfo, mapOutputType(methodScope.info));
}

if (methodScope.info.nillableArgs)
node.appendChild(SyntaxKey::NillableInfo, methodScope.info.nillableArgs);

if (methodScope.info.hints)
node.appendChild(SyntaxKey::Hints, methodScope.info.hints);

Expand Down Expand Up @@ -4641,7 +4661,7 @@ void Compiler :: declareArgumentAttributes(MethodScope& scope, SyntaxNode node,
bool declarationMode)
{
SyntaxNode current = node.firstChild();
TypeAttributes attributes = { false, false, false };
TypeAttributes attributes = { };
while (current != SyntaxKey::None) {
switch (current.key) {
case SyntaxKey::Type:
Expand All @@ -4652,8 +4672,8 @@ void Compiler :: declareArgumentAttributes(MethodScope& scope, SyntaxNode node,
// if it is a template type attribute
typeInfo = resolveTypeAttribute(scope, current, attributes, declarationMode, false);
break;
case SyntaxKey::ArrayType:
case SyntaxKey::NullableType:
case SyntaxKey::ArrayType:
// if it is a type attribute
typeInfo = resolveTypeScope(scope, current, attributes, declarationMode, false);
break;
Expand Down Expand Up @@ -5528,8 +5548,8 @@ TypeInfo Compiler :: resolveTypeScope(Scope& scope, SyntaxNode node, TypeAttribu
case SyntaxKey::reference:
elementRef = resolveTypeIdentifier(scope, current.identifier(), node.key, declarationMode, allowRole);
break;
case SyntaxKey::ArrayType:
case SyntaxKey::NullableType:
case SyntaxKey::ArrayType:
elementRef = resolvePrimitiveType(*scope.moduleScope,
resolveTypeAttribute(scope, current, attributes, declarationMode, allowRole), declarationMode);
break;
Expand Down Expand Up @@ -6473,7 +6493,7 @@ ref_t Compiler :: compileExtensionDispatcher(BuildTreeWriter& writer, NamespaceS
for(auto it = scope.extensions.getIt(genericMessage); !it.eof(); it = scope.extensions.nextIt(genericMessage, it)) {
auto extInfo = *it;

methods.add(extInfo.value2, { false, 0, 0, genericMessage | FUNCTION_MESSAGE, 0 });
methods.add(extInfo.value2, { false, 0, 0, genericMessage | FUNCTION_MESSAGE, 0, 0 });
targets.add(extInfo.value2, extInfo.value1);
}

Expand Down Expand Up @@ -9194,7 +9214,7 @@ void Compiler :: compileClosureClass(BuildTreeWriter& writer, ClassScope& scope,
classWriter.newNode(SyntaxKey::Class, scope.reference);

SyntaxNode classNode = classWriter.CurrentNode();
injectVirtualMultimethod(classNode, SyntaxKey::Method, scope, scope.reference, scope.info, multiMethod);
injectVirtualMultimethod(classNode, SyntaxKey::Method, scope, scope.reference, scope.info, multiMethod, 0);

classWriter.closeNode();
classWriter.closeNode();
Expand Down Expand Up @@ -10055,7 +10075,7 @@ inline bool isSingleDispatch(SyntaxNode node, SyntaxKey methodType, mssg_t messa
}

bool Compiler :: injectVirtualStrongTypedMultimethod(SyntaxNode classNode, SyntaxKey methodType, Scope& scope,
mssg_t message, mssg_t resendMessage, TypeInfo outputInfo, Visibility visibility, bool isExtension)
mssg_t message, mssg_t resendMessage, TypeInfo outputInfo, Visibility visibility, bool isExtension, int nillableArgs)
{
bool variadicOne = (getFlags(resendMessage) & PREFIX_MESSAGE_MASK) == VARIADIC_MESSAGE;

Expand Down Expand Up @@ -10095,6 +10115,8 @@ bool Compiler :: injectVirtualStrongTypedMultimethod(SyntaxNode classNode, Synta

String<char, 10> arg;
for (size_t i = 0; i < len; i++) {
bool isNillable = test(nillableArgs, 1 << i);

arg.copy("$");
arg.appendInt((int)i);

Expand All @@ -10108,7 +10130,14 @@ bool Compiler :: injectVirtualStrongTypedMultimethod(SyntaxNode classNode, Synta
castObject.appendChild(SyntaxKey::Attribute, V_CONVERSION);
castObject.appendChild(SyntaxKey::Type, signArgs[i]);
castNode.appendChild(SyntaxKey::Message);
castNode.appendChild(SyntaxKey::Expression).appendChild(SyntaxKey::Object).appendChild(SyntaxKey::identifier, arg.str());
if (isNillable) {
SyntaxNode isNilOp = castNode.appendChild(SyntaxKey::Expression).appendChild(SyntaxKey::IsNilOperation);
isNilOp.appendChild(SyntaxKey::Object).appendChild(SyntaxKey::identifier, arg.str());
SyntaxNode nilObject = isNilOp.appendChild(SyntaxKey::Expression).appendChild(SyntaxKey::Object);
nilObject.appendChild(SyntaxKey::Attribute, V_FORWARD);
nilObject.appendChild(SyntaxKey::identifier, NILVALUE_FORWARD);
}
else castNode.appendChild(SyntaxKey::Expression).appendChild(SyntaxKey::Object).appendChild(SyntaxKey::identifier, arg.str());
}
else operationNode.appendChild(SyntaxKey::Expression).appendChild(SyntaxKey::Object).appendChild(SyntaxKey::identifier, arg.str());
}
Expand Down Expand Up @@ -10143,7 +10172,8 @@ bool Compiler :: injectVirtualStrongTypedMultimethod(SyntaxNode classNode, Synta
}

void Compiler :: injectVirtualMultimethod(SyntaxNode classNode, SyntaxKey methodType, Scope& scope,
ref_t targetRef, ClassInfo& info, mssg_t message, bool inherited, TypeInfo outputInfo, Visibility visibility)
ref_t targetRef, ClassInfo& info, mssg_t message, bool inherited, TypeInfo outputInfo,
Visibility visibility, int nillableArgs)
{
bool isExtension = test(info.header.flags, elExtension);

Expand All @@ -10160,7 +10190,7 @@ void Compiler :: injectVirtualMultimethod(SyntaxNode classNode, SyntaxKey method
// !! temporally do not support variadic arguments
if (isSingleDispatch(classNode, methodType, message, resendMessage) &&
injectVirtualStrongTypedMultimethod(classNode, methodType, scope, message, resendMessage,
outputInfo, visibility, isExtension))
outputInfo, visibility, isExtension, nillableArgs))
{
// mark the message as a signle dispatcher if the class is sealed / closed / class class
// and default multi-method was not explicitly declared
Expand Down Expand Up @@ -10485,7 +10515,7 @@ void Compiler :: declareModuleExtensionDispatcher(NamespaceScope& scope, SyntaxN
if (retrieveIndex(genericMethods, genericMessage) == -1)
genericMethods.add(genericMessage);

methods.add(extInfo.value2, { false, 0, 0, genericMessage | FUNCTION_MESSAGE, 0 });
methods.add(extInfo.value2, { false, 0, 0, genericMessage | FUNCTION_MESSAGE, 0, 0 });
targets.add(extInfo.value2, extInfo.value1);
}
}
Expand Down
Loading

0 comments on commit 54a7c4c

Please sign in to comment.