Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions nCompiler/R/NC_CompilerClass.R
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,14 @@ NC_CompilerClass <- R6::R6Class(
resolveTBDsymbols(symbolTable,
NCgenerator,
project_env = project_env)
## Add 'self' so method bodies can reference the current object.
## genCppVar() gives a special cppVar case where generate() returns ""
## and generateUse returns "nC_shared_from_this()".
symbolTable$addSymbol(symbolSelf$new(
name = 'self',
type = NCinternals(NCgenerator)$cpp_classname,
NCgenerator = NCgenerator
))
setupMethodSymbolTables()
}
},
Expand Down
10 changes: 7 additions & 3 deletions nCompiler/R/compile_generateCpp.R
Original file line number Diff line number Diff line change
Expand Up @@ -378,8 +378,10 @@ cppOutputMemberData <- function(code, symTab) {
inGenCppEnv(
## Member(A, x) -> A.x
Member <- function(code, symTab, connector = '.') {
isSelf <- code$args[[1]]$name == 'self' && inherits(code$args[[1]]$type, "symbolSelf")
objOutput <- if(isSelf) 'this' else compile_generateCpp(code$args[[1]], symTab)
paste0( '(',
compile_generateCpp(code$args[[1]], symTab),
objOutput,
')', connector, code$args[[2]]$name)
}
)
Expand All @@ -395,7 +397,9 @@ inGenCppEnv(
## This differs from old system
## Method(A, foo, x) -> A.foo(x)
Method <- function(code, symTab, connector = '.') {
obj <- paste0('(', compile_generateCpp(code$args[[1]], symTab), ')', connector)
isSelf <- code$args[[1]]$name == 'self' && inherits(code$args[[1]]$type, "symbolSelf")
objOutput <- if(isSelf) 'this' else compile_generateCpp(code$args[[1]], symTab)
objPart <- paste0('(', objOutput, ')', connector)
opString <- getCppString(code$args[[2]])
methodCall <- paste0(
opString, '(',
Expand All @@ -404,7 +408,7 @@ inGenCppEnv(
collapse = ', '
), ')'
)
paste0(obj, methodCall)
paste0(objPart, methodCall)
}
)
inGenCppEnv(
Expand Down
69 changes: 21 additions & 48 deletions nCompiler/R/compile_labelAbstractTypes.R
Original file line number Diff line number Diff line change
Expand Up @@ -387,13 +387,12 @@ inLabelAbstractTypesEnv(
)
code$type <- returnSym
## Logically it might seem this should become ->method.
## However it appears in nFunction(->member(A, foo), x) for A->foo(x).
## In stage generateCpp, the nFunction packs the arguments after A->foo,
## However it appears in chainedCall(->member(A, foo), x) for A->foo(x).
## In stage generateCpp, the chainedCall packs the arguments after A->foo,
## so we mark that here as a member.
code$name <- '->member'
code$args[[2]]$aux$obj_internals <- obj_internals
code$args[[2]]$aux$nFunctionName <- innerName
#code$args[[2]]$name <- NFinternals(method)$cpp_code_name
code$args[[2]]$name <- NCinternals(code$args[[1]]$type$NCgenerator)$all_methodName_to_cpp_code_name[[innerName]]

obj_internals <- NULL
Expand All @@ -407,49 +406,10 @@ inLabelAbstractTypesEnv(
code$type <- symbol$clone(deep = TRUE)
code$name <- '->member'
}
## TO-DO: Handle special case of "new", or put it in
## the nClass symbol table.
if(length(inserts) == 0) NULL else inserts
}
)

## a$b would become nClass_member(a, b)
## a$b$foo(x) would become chainedCall(`$`(`$`(a, b), foo), x)
## which would become nFunction( nClass_member(nClass$member(a, b), foo) , x)

## Called by Generic_nFunction and Generic_nFunction_method
## This converts foo(x) to nFunction(foo, x)
## if foo is either an nFunction or a method of the current class
## inLabelAbstractTypesEnv(
## convert_nFunction_or_method_AST <-
## function(code, obj) {
## nFunctionName <- code$name
## ## Note that the string `nFunction` matches the operatorDef entry.
## ## Therefore the change-of-name here will automatically trigger use of
## ## the 'nFunction' operatorDef in later stages.
## code$name <- 'nFunction'
## cpp_code_name <- NFinternals(obj)$cpp_code_name
## fxnNameExpr <- exprClass$new(name = cpp_code_name, isName = TRUE,
## isCall = FALSE, isLiteral = FALSE, isAssign = FALSE)
## ## We may need to add content to this symbol if
## ## necessary for later processing steps.
## fxnNameExpr$type <- symbolNF$new(name = nFunctionName)
## insertArg(code, 1, fxnNameExpr)
## ## TO-DO: Add error-trapping of argument types
## returnSym <- NFinternals(obj)$returnSym
## if(is.null(returnSym))
## stop(
## exprClassProcessingErrorMsg(
## code, paste('In convert_nFunction_or_method_AST: the nFunction (or method) ',
## code$name,
## ' does not have a valid returnType.')
## ), call. = FALSE
## )
## code$type <- returnSym$clone() ## Not sure if a clone is needed, but it seems safer to make one.
## invisible(NULL)
## }
## )

## Called by main compile_labelAbstractTypes loop
## This converts use of the function foo as an object to
## nFunctionRef(foo, namespace)
Expand Down Expand Up @@ -1495,12 +1455,25 @@ inLabelAbstractTypesEnv(
insertions <- recurse_labelAbstractTypes(code, symTab, auxEnv, handlingInfo)
code$type <- code$args[[1]]$type
# see if the returned object differs from the nFunction's return type
if(!identical(class(auxEnv$returnSymbol), class(code$type))) {
warning(exprClassProcessingErrorMsg(
code,
"Object type for return() does not match the nFunction's return type."
),
call. = FALSE)
# To-do: We could look at the NCgenerator class hierarchy to actually
# determine validity of returned type. Instead here we just
# see if both are symbolNC, with a special-case check for a symbolSelf (or other case)
# that inherits from symbolNC
if(inherits(auxEnv$returnSymbol, "symbolNC")) {
if(!inherits(code$type, class(auxEnv$returnSymbol)[1]))
warning(exprClassProcessingErrorMsg(
code,
"Object nClass type for return() does not match the nFunction's return type."
),
call. = FALSE)
} else {
if(!identical(class(auxEnv$returnSymbol), class(code$type))) {
warning(exprClassProcessingErrorMsg(
code,
"Object type for return() does not match the nFunction's return type."
),
call. = FALSE)
}
}
if(inherits(auxEnv$returnSymbol, "symbolBasic")) {
# problem if number of dimensions differs
Expand Down
16 changes: 16 additions & 0 deletions nCompiler/R/cppDefs_variables.R
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,22 @@ cppVarClass <- R6::R6Class(
)
)

cppVarSelfClass <- R6::R6Class(
classname = 'cppVarSelfClass',
inherit = cppVarClass,
portable = TRUE,
public = list(
initialize = function(...) {
super$initialize(...)
self$name <- "self"
},
generate = function(printName = character(), ...) {
character()
},
generateUse = function(...) "nC_shared_from_this()"
)
)

cppVar2cppVarFull <- function(cppVar, ...) {
ans <- cppVarFullClass$new(name = cppVar$name,
baseType = cppVar$baseType,
Expand Down
25 changes: 25 additions & 0 deletions nCompiler/R/symbolTable.R
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,31 @@ symbolNC <- R6::R6Class(
)
)

## Symbol for `self` inside nClass method bodies.
## generateUse() emits nC_shared_from_this() so that `self` used as a value
## (passed as argument or returned) produces a std::shared_ptr<ClassName>.
## For method calls (self$method(x)), the DollarSign handler in
## compile_labelAbstractTypes uses the inherited NCgenerator for lookup,
## and PtrMember in compile_generateCpp emits (nC_shared_from_this())->method(x).
symbolSelf <- R6::R6Class(
classname = "symbolSelf",
inherit = symbolNC,
portable = TRUE,
public = list(
initialize = function(name, type, NCgenerator, isArg = FALSE) {
super$initialize(name = name,
type = type,
NCgenerator = NCgenerator,
isArg = isArg)
},
# Note that the genCppOutput handlers for 'Method' and 'Member'
# intercept this. If they see a name "self" with type that inherits from "symbolSelf",
# they will emit "this" instead of generating a cppVar for it.
# The cppVar for self is only used if self is used as a value, such as in an argument or return value.
genCppVar = function() cppVarSelfClass$new()
)
)

## type is the unique ID of the NCgenerator.
## same value as for a symbolNC for an object of the class.
symbolNCgenerator <- R6::R6Class(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,14 @@ class genericInterfaceBaseC {
};
};

// Forward declaration needed for class_from_interface below
template<class T> class genericInterfaceC;

// Extracts ClassName from genericInterfaceC<ClassName>, used by interface_resolver
// to determine the owned type for nC_shared_from_this().
template<typename T> struct class_from_interface { using type = void; };
template<typename T> struct class_from_interface<genericInterfaceC<T>> { using type = T; };

// FirstDerived and interface_resolver<> designed with help from Google Gemini
// Helper template to find the first type that inherits from Base
template <typename T, typename... Rest>
Expand All @@ -136,13 +144,65 @@ struct FirstGenericDerived<T> {
>;
};

// General case (2+ template args): derived nClass.
// nC_shared_from_this() casts the inherited shared_from_this() result to
// shared_ptr<OwnedType>, where OwnedType is the most-derived class
// (extracted from the first template arg, which is always genericInterfaceC<ClassName>).
// The enable_shared_from_this lives only in the single-arg (root) specialization below;
// it is inherited through the base-class chain and initialized correctly by
// shared_ptr<DerivedClass> because DerivedClass is derived from enable_shared_from_this<RootClass>.
template <typename... Bases>
class interface_resolver : public Bases..., virtual public genericInterfaceBaseC
{
private:
using FirstFound = typename FirstGenericDerived<Bases...>::type;
using OwnedType = typename class_from_interface<FirstFound>::type;

public:
std::shared_ptr<OwnedType> nC_shared_from_this() {
return std::static_pointer_cast<OwnedType>(this->shared_from_this());
}
const name2access_type& get_name2access() const override {
return FirstFound::get_name2access();
}
std::unique_ptr<ETaccessorBase> access(const std::string &name) override {
return FirstFound::access(name);
}
SEXP get_value(const std::string &name) const override {
return FirstFound::get_value(name);
}
void set_all_values(SEXP Robj) override {
FirstFound::set_all_values(Robj);
}
void set_value(const std::string &name, SEXP Svalue) override {
FirstFound::set_value(name, Svalue);
}
SEXP call_method(const std::string &name, SEXP Sargs) override {
return FirstFound::call_method(name, Sargs);
}
SEXP make_deserialized_return_SEXP() override {
return FirstFound::make_deserialized_return_SEXP();
}
};

// Single-arg specialization: root nClass (no nClass parent).
// Adds enable_shared_from_this<OwnedType> so that shared_ptr<ClassName>
// correctly initialises the weak_ptr used by shared_from_this().
// nC_shared_from_this() is a trivial cast here (same type).
template<typename First>
class interface_resolver<First> :
public First,
virtual public genericInterfaceBaseC,
public std::enable_shared_from_this<typename class_from_interface<First>::type>
{
private:
using FirstFound = First;
using OwnedType = typename class_from_interface<First>::type;

public:
std::shared_ptr<OwnedType> nC_shared_from_this() {
return std::enable_shared_from_this<OwnedType>::shared_from_this();
}
const name2access_type& get_name2access() const override {
return FirstFound::get_name2access();
}
Expand All @@ -166,6 +226,7 @@ class interface_resolver : public Bases..., virtual public genericInterfaceBaseC
}
};

// Empty specialization: nClass with no generic interface (no enable_shared_from_this).
template<>
class interface_resolver<> : virtual public genericInterfaceBaseC
{
Expand Down
Loading
Loading