From 830671c2210191f69ddc403cc12f5454bb55b0f0 Mon Sep 17 00:00:00 2001 From: Tim Foley Date: Fri, 21 Feb 2020 10:39:05 -0800 Subject: Add surface syntax for "this type" (#1236) Within the context of an aggregate type (or an `extension` of one), the programmer can use `this` to refer to the "current" instance of the surrounding type, but there is no easy way to utter the name of the type itself. This is especially relevant inside of an `interface`, where the type of `this` isn't actually the `interface` type, but rather a placeholder for the as-yet-unknown concrete type that will implement the interface. This change adds a keyword `This` that works similarly to `this`, but names the current *type* instead of the current instance. It can be used to declare things like binary methods or factory functions in an interface: ``` interface IBasicMathType { This absoluteValue(); This sumWith(This left); } T doSomeMath(T value) { return value.sumWith(value.absoluteValue()); } ``` The `This` type is consistent with the type named `Self` in Rust and Swift (where Rust/Swift use `self` instead of `this`). Other names could be considered (e.g., `ThisType`) if we find that users don't like the name in this change. --- source/slang/slang-check-expr.cpp | 30 ++++++++++++++++++++++++++++++ source/slang/slang-check-impl.h | 1 + source/slang/slang-diagnostic-defs.h | 1 + source/slang/slang-expr-defs.h | 8 ++++++++ source/slang/slang-lower-to-ir.cpp | 6 ++++++ source/slang/slang-mangle.cpp | 5 +++++ source/slang/slang-parser.cpp | 19 +++++++++++++++++++ 7 files changed, 70 insertions(+) (limited to 'source') diff --git a/source/slang/slang-check-expr.cpp b/source/slang/slang-check-expr.cpp index ebc3a3ef9..524bab4e4 100644 --- a/source/slang/slang-check-expr.cpp +++ b/source/slang/slang-check-expr.cpp @@ -1677,6 +1677,12 @@ namespace Slang expr->type.IsLeftValue = true; } } + else if( auto typeOrExtensionDecl = as(containerDecl) ) + { + expr->type.type = calcThisType(makeDeclRef(typeOrExtensionDecl)); + return expr; + } +#if 0 else if (auto aggTypeDecl = as(containerDecl)) { ensureDecl(aggTypeDecl, DeclCheckState::CanUseAsType); @@ -1706,6 +1712,7 @@ namespace Slang expr->type.type = extensionDecl->targetType.type; return expr; } +#endif scope = scope->parent; } @@ -1713,4 +1720,27 @@ namespace Slang getSink()->diagnose(expr, Diagnostics::thisExpressionOutsideOfTypeDecl); return CreateErrorExpr(expr); } + + RefPtr SemanticsExprVisitor::visitThisTypeExpr(ThisTypeExpr* expr) + { + auto scope = expr->scope; + while (scope) + { + auto containerDecl = scope->containerDecl; + if( auto typeOrExtensionDecl = as(containerDecl) ) + { + auto thisType = calcThisType(makeDeclRef(typeOrExtensionDecl)); + auto thisTypeType = getTypeType(thisType); + + expr->type.type = thisTypeType; + return expr; + } + + scope = scope->parent; + } + + getSink()->diagnose(expr, Diagnostics::thisTypeOutsideOfTypeDecl); + return CreateErrorExpr(expr); + } + } diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h index 764f0526a..9363066d1 100644 --- a/source/slang/slang-check-impl.h +++ b/source/slang/slang-check-impl.h @@ -1363,6 +1363,7 @@ namespace Slang RefPtr visitInitializerListExpr(InitializerListExpr* expr); RefPtr visitThisExpr(ThisExpr* expr); + RefPtr visitThisTypeExpr(ThisTypeExpr* expr); }; struct SemanticsStmtVisitor diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h index 06031c089..8bcdb9a09 100644 --- a/source/slang/slang-diagnostic-defs.h +++ b/source/slang/slang-diagnostic-defs.h @@ -384,6 +384,7 @@ DIAGNOSTIC(38100, Error, typeDoesntImplementInterfaceRequirement, "type '$0' doe DIAGNOSTIC(38101, Error, thisExpressionOutsideOfTypeDecl, "'this' expression can only be used in members of an aggregate type") DIAGNOSTIC(38102, Error, initializerNotInsideType, "an 'init' declaration is only allowed inside a type or 'extension' declaration") DIAGNOSTIC(38102, Error, accessorMustBeInsideSubscriptOrProperty, "an accessor declaration is only allowed inside a subscript or property declaration") +DIAGNOSTIC(38103, Error, thisTypeOutsideOfTypeDecl, "'This' type can only be used inside of an aggregate type") DIAGNOSTIC(38020, Error, mismatchEntryPointTypeArgument, "expecting $0 entry-point type arguments, provided $1.") DIAGNOSTIC(38021, Error, typeArgumentForGenericParameterDoesNotConformToInterface, "type argument `$0` for generic parameter `$1` does not conform to interface `$2`.") diff --git a/source/slang/slang-expr-defs.h b/source/slang/slang-expr-defs.h index 6cd893302..a7105b07b 100644 --- a/source/slang/slang-expr-defs.h +++ b/source/slang/slang-expr-defs.h @@ -203,4 +203,12 @@ SYNTAX_CLASS(TaggedUnionTypeExpr, Expr) RAW( List caseTypes; ) +END_SYNTAX_CLASS() + + /// A type expression of the form `This` + /// + /// Refers to the type of `this` in the current context. + /// +SYNTAX_CLASS(ThisTypeExpr, Expr) + FIELD(RefPtr, scope); END_SYNTAX_CLASS() diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index 360376b73..3cd3f73e3 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -3004,6 +3004,12 @@ struct ExprLoweringVisitorBase : ExprVisitor UNREACHABLE_RETURN(LoweredValInfo()); } + LoweredValInfo visitThisTypeExpr(ThisTypeExpr* /*expr*/) + { + SLANG_UNIMPLEMENTED_X("this-type expression during code generation"); + UNREACHABLE_RETURN(LoweredValInfo()); + } + LoweredValInfo visitAssignExpr(AssignExpr* expr) { // Because our representation of lowered "values" diff --git a/source/slang/slang-mangle.cpp b/source/slang/slang-mangle.cpp index fbeb5c9bf..5d9320f3d 100644 --- a/source/slang/slang-mangle.cpp +++ b/source/slang/slang-mangle.cpp @@ -151,6 +151,11 @@ namespace Slang } emitRaw(context, "U"); } + else if( auto thisType = dynamicCast(type) ) + { + emitRaw(context, "t"); + emitQualifiedName(context, thisType->interfaceDeclRef); + } else { SLANG_UNEXPECTED("unimplemented case in mangling"); diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp index e5a1ad576..288962cd8 100644 --- a/source/slang/slang-parser.cpp +++ b/source/slang/slang-parser.cpp @@ -1794,6 +1794,19 @@ namespace Slang return parseTaggedUnionType(parser); } + /// Parse a `This` type expression + static RefPtr parseThisTypeExpr(Parser* parser) + { + RefPtr expr = new ThisTypeExpr(); + expr->scope = parser->currentScope; + return expr; + } + + static RefPtr parseThisTypeExpr(Parser* parser, void* /*userData*/) + { + return parseThisTypeExpr(parser); + } + static TypeSpec parseTypeSpec(Parser* parser) { TypeSpec typeSpec; @@ -1848,6 +1861,11 @@ namespace Slang typeSpec.expr = parseTaggedUnionType(parser); return typeSpec; } + else if(AdvanceIf(parser, "This")) + { + typeSpec.expr = parseThisTypeExpr(parser); + return typeSpec; + } Token typeName = parser->ReadToken(TokenType::Identifier); @@ -4995,6 +5013,7 @@ namespace Slang addBuiltinSyntax(session, scope, #KEYWORD, &CALLBACK) EXPR(this, parseThisExpr); + EXPR(This, parseThisTypeExpr); EXPR(true, parseTrueExpr); EXPR(false, parseFalseExpr); EXPR(__TaggedUnion, parseTaggedUnionType); -- cgit v1.2.3