From 3a5214b65b2a5efdbcf9bf6fb4d7603e9ee63234 Mon Sep 17 00:00:00 2001 From: Tim Foley Date: Thu, 18 Oct 2018 09:30:38 -0700 Subject: Add support for static methods in interfaces (#680) This change allows an interface to include `static` methods as requirements, so that types that conform to the interface will need to satisfy the requirement with a `static` method. The essence of the check is simple: when checking that a method satisfies a requirement, we enforce that both are `static` or both are non-`static`. Making that simple change and adding a test change broke a few other places in the compiler that this change tries to fix. The main fix is to handle cases where we might look up an "effectively static" member of a type through an instance, and to make sure that we replace the instance-based lookup with type-based lookup. There was already logic along these lines in `lower-to-ir.cpp`, so this change centralizes it in `check.cpp` where it seems to logically belong. --- source/slang/check.cpp | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) (limited to 'source/slang/check.cpp') diff --git a/source/slang/check.cpp b/source/slang/check.cpp index 985c5c0b7..47713e6cc 100644 --- a/source/slang/check.cpp +++ b/source/slang/check.cpp @@ -9,6 +9,75 @@ namespace Slang { + + /// Should the given `decl` nested in `parentDecl` be treated as a static rather than instance declaration? + bool isEffectivelyStatic( + Decl* decl, + ContainerDecl* parentDecl) + { + // Things at the global scope are always "members" of their module. + // + if(parentDecl->As()) + return false; + + // Anything explicitly marked `static` and not at module scope + // counts as a static rather than instance declaration. + // + if(decl->HasModifier()) + return true; + + // Next we need to deal with cases where a declaration is + // effectively `static` even if the language doesn't make + // the user say so. Most languages make the default assumption + // that nested types are `static` even if they don't say + // so (Java is an exception here, perhaps due to some + // influence from the Scandanavian OOP tradition of Beta/gbeta). + // + if(dynamic_cast(decl)) + return true; + if(dynamic_cast(decl)) + return true; + + // Things nested inside functions may have dependencies + // on values from the enclosing scope, but this needs to + // be dealt with via "capture" so they are also effectively + // `static` + // + if(dynamic_cast(parentDecl)) + return true; + + // Type constraint declarations are used in member-reference + // context as a form of casting operation, so we treat them + // as if they are instance members. This is a bit of a hack, + // but it achieves the result we want until we have an + // explicit representation of up-cast operations in the + // AST. + // + if(decl->As()) + return false; + + return false; + } + + /// Should the given `decl` be treated as a static rather than instance declaration? + bool isEffectivelyStatic( + Decl* decl) + { + // For the purposes of an ordinary declaration, when determining if + // it is static or per-instance, the "parent" declaration we really + // care about is the next outer non-generic declaration. + // + // TODO: This idiom of getting the "next outer non-generic declaration" + // comes up just enough that we should probably have a convenience + // function for it. + + auto parentDecl = decl->ParentDecl; + if(auto genericDecl = parentDecl->As()) + parentDecl = genericDecl->ParentDecl; + + return isEffectivelyStatic(decl, parentDecl); + } + // A flat representation of basic types (scalars, vectors and matrices) // that can be used as lookup key in caches struct BasicTypeKey @@ -416,6 +485,22 @@ namespace Slang expr->declRef = declRef; return expr; } + else if(isEffectivelyStatic(declRef.getDecl())) + { + // Extract the type of the baseExpr + auto baseExprType = baseExpr->type.type; + RefPtr baseTypeExpr = new SharedTypeExpr(); + baseTypeExpr->base.type = baseExprType; + baseTypeExpr->type = new TypeType(baseExprType); + + auto expr = new StaticMemberExpr(); + expr->loc = loc; + expr->type = type; + expr->BaseExpression = baseTypeExpr; + expr->name = declRef.GetName(); + expr->declRef = declRef; + return expr; + } else { // If the base expression wasn't a type, then this @@ -2246,6 +2331,13 @@ namespace Slang return false; } + if(satisfyingMemberDeclRef.getDecl()->HasModifier() + != requiredMemberDeclRef.getDecl()->HasModifier()) + { + // A `static` method can't satisfy a non-`static` requirement and vice versa. + return false; + } + // TODO: actually implement matching here. For now we'll // just pretend that things are satisfied in order to make progress.. witnessTable->requirementDictionary.Add( -- cgit v1.2.3