Skip to content

Commit

Permalink
Initial support for aliases. (#1262)
Browse files Browse the repository at this point in the history
This supports aliases for types (including interfaces), functions, parameterized types, instance member names, and interface member names.

Co-authored-by: Jon Meow <[email protected]>
  • Loading branch information
zygoloid and jonmeow authored May 16, 2022
1 parent 957188e commit 471bb56
Show file tree
Hide file tree
Showing 22 changed files with 458 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,13 @@ repos:
hooks:
- id: codespell
args: ['-I', '.codespell_ignore', '--uri-ignore-words-list', '*']
# Test data may contain intentional misspellings, as well as short,
# meaningless identifiers that codespell incorrectly identifies as
# typos but that we would want to detect in other contexts.
exclude: |
(?x)^(
.*/testdata/.*
)$
- repo: https://github.com/google/pre-commit-tool-hooks
rev: cb78d9293306d9f737c64d9702bbaa88e157caaa # frozen: v1.2.2
hooks:
Expand Down
6 changes: 6 additions & 0 deletions common/fuzzing/carbon.proto
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,11 @@ message ImplDeclaration {
repeated Declaration members = 4;
}

message AliasDeclaration {
optional string name = 1;
optional Expression target = 2;
}

message Declaration {
oneof kind {
FunctionDeclaration function = 1;
Expand All @@ -340,6 +345,7 @@ message Declaration {
VariableDeclaration variable = 4;
InterfaceDeclaration interface = 5;
ImplDeclaration impl = 6;
AliasDeclaration alias = 7;
}
}

Expand Down
10 changes: 10 additions & 0 deletions common/fuzzing/proto_to_carbon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,16 @@ static auto DeclarationToCarbon(const Fuzzing::Declaration& declaration,
out << "}";
break;
}

case Fuzzing::Declaration::kAlias: {
const auto& alias = declaration.alias();
out << "alias ";
IdentifierToCarbon(alias.name(), out);
out << " = ";
ExpressionToCarbon(alias.target(), out);
out << ";";
break;
}
}
}

Expand Down
1 change: 1 addition & 0 deletions explorer/ast/ast_rtti.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ abstract class Declaration : AstNode;
class VariableDeclaration : Declaration;
class InterfaceDeclaration : Declaration;
class ImplDeclaration : Declaration;
class AliasDeclaration : Declaration;
class ImplBinding : AstNode;
class AlternativeSignature : AstNode;
abstract class Statement : AstNode;
Expand Down
16 changes: 16 additions & 0 deletions explorer/ast/declaration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ void Declaration::Print(llvm::raw_ostream& out) const {
out << "Self";
break;
}

case DeclarationKind::AliasDeclaration: {
const auto& alias = cast<AliasDeclaration>(*this);
PrintID(out);
out << " = " << alias.target() << ";\n";
break;
}
}
}

Expand Down Expand Up @@ -127,6 +134,12 @@ void Declaration::PrintID(llvm::raw_ostream& out) const {
out << "Self";
break;
}

case DeclarationKind::AliasDeclaration: {
const auto& alias = cast<AliasDeclaration>(*this);
out << "alias " << alias.name();
break;
}
}
}

Expand All @@ -146,6 +159,9 @@ auto GetName(const Declaration& declaration) -> std::optional<std::string> {
return std::nullopt;
case DeclarationKind::SelfDeclaration:
return cast<SelfDeclaration>(declaration).name();
case DeclarationKind::AliasDeclaration: {
return cast<AliasDeclaration>(declaration).name();
}
}
}

Expand Down
24 changes: 24 additions & 0 deletions explorer/ast/declaration.h
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,30 @@ class ImplDeclaration : public Declaration {
std::vector<Nonnull<const ImplBinding*>> impl_bindings_;
};

class AliasDeclaration : public Declaration {
public:
using ImplementsCarbonValueNode = void;

explicit AliasDeclaration(SourceLocation source_loc, const std::string& name,
Nonnull<Expression*> target)
: Declaration(AstNodeKind::AliasDeclaration, source_loc),
name_(name),
target_(target) {}

static auto classof(const AstNode* node) -> bool {
return InheritsFromAliasDeclaration(node->kind());
}

auto name() const -> const std::string { return name_; }
auto target() const -> const Expression& { return *target_; }
auto target() -> Expression& { return *target_; }
auto value_category() const -> ValueCategory { return ValueCategory::Let; }

private:
std::string name_;
Nonnull<Expression*> target_;
};

// Return the name of a declaration, if it has one.
auto GetName(const Declaration&) -> std::optional<std::string>;

Expand Down
8 changes: 8 additions & 0 deletions explorer/fuzzing/ast_to_proto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,14 @@ static auto DeclarationToProto(const Declaration& declaration)
case DeclarationKind::SelfDeclaration: {
CARBON_FATAL() << "Unreachable SelfDeclaration in DeclarationToProto().";
}

case DeclarationKind::AliasDeclaration: {
const auto& alias = cast<AliasDeclaration>(declaration);
auto* alias_proto = declaration_proto.mutable_alias();
alias_proto->set_name(alias.name());
*alias_proto->mutable_target() = ExpressionToProto(alias.target());
break;
}
}
return declaration_proto;
}
Expand Down
1 change: 1 addition & 0 deletions explorer/interpreter/interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1400,6 +1400,7 @@ auto Interpreter::StepDeclaration() -> ErrorOr<Success> {
case DeclarationKind::InterfaceDeclaration:
case DeclarationKind::ImplDeclaration:
case DeclarationKind::SelfDeclaration:
case DeclarationKind::AliasDeclaration:
// These declarations have no run-time effects.
return todo_.FinishAction();
}
Expand Down
1 change: 1 addition & 0 deletions explorer/interpreter/resolve_control_flow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ auto ResolveControlFlow(Nonnull<Declaration*> declaration) -> ErrorOr<Success> {
case DeclarationKind::ChoiceDeclaration:
case DeclarationKind::VariableDeclaration:
case DeclarationKind::SelfDeclaration:
case DeclarationKind::AliasDeclaration:
// do nothing
break;
}
Expand Down
11 changes: 11 additions & 0 deletions explorer/interpreter/resolve_names.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ static auto AddExposedNames(const Declaration& declaration,
CARBON_RETURN_IF_ERROR(enclosing_scope.Add("Self", &self));
break;
}
case DeclarationKind::AliasDeclaration: {
auto& alias = cast<AliasDeclaration>(declaration);
CARBON_RETURN_IF_ERROR(enclosing_scope.Add(alias.name(), &alias));
break;
}
}
return Success();
}
Expand Down Expand Up @@ -446,6 +451,12 @@ static auto ResolveNames(Declaration& declaration, StaticScope& enclosing_scope)
case DeclarationKind::SelfDeclaration: {
CARBON_FATAL() << "Unreachable: resolving names for `Self` declaration";
}

case DeclarationKind::AliasDeclaration: {
CARBON_RETURN_IF_ERROR(ResolveNames(
cast<AliasDeclaration>(declaration).target(), enclosing_scope));
break;
}
}
return Success();
}
Expand Down
75 changes: 75 additions & 0 deletions explorer/interpreter/type_checker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2409,6 +2409,72 @@ auto TypeChecker::TypeCheckChoiceDeclaration(
return Success();
}

static bool IsValidTypeForAliasTarget(Nonnull<const Value*> type) {
switch (type->kind()) {
case Value::Kind::IntValue:
case Value::Kind::FunctionValue:
case Value::Kind::BoundMethodValue:
case Value::Kind::PointerValue:
case Value::Kind::LValue:
case Value::Kind::BoolValue:
case Value::Kind::StructValue:
case Value::Kind::NominalClassValue:
case Value::Kind::AlternativeValue:
case Value::Kind::TupleValue:
case Value::Kind::Witness:
case Value::Kind::ParameterizedEntityName:
case Value::Kind::MemberName:
case Value::Kind::BindingPlaceholderValue:
case Value::Kind::AlternativeConstructorValue:
case Value::Kind::ContinuationValue:
case Value::Kind::StringValue:
CARBON_FATAL() << "type of alias target is not a type";

case Value::Kind::AutoType:
case Value::Kind::VariableType:
CARBON_FATAL() << "pattern type in alias target";

case Value::Kind::IntType:
case Value::Kind::BoolType:
case Value::Kind::PointerType:
case Value::Kind::StaticArrayType:
case Value::Kind::StructType:
case Value::Kind::NominalClassType:
case Value::Kind::ChoiceType:
case Value::Kind::ContinuationType:
case Value::Kind::StringType:
return false;

case Value::Kind::FunctionType:
case Value::Kind::InterfaceType:
case Value::Kind::TypeType:
case Value::Kind::TypeOfClassType:
case Value::Kind::TypeOfInterfaceType:
case Value::Kind::TypeOfChoiceType:
case Value::Kind::TypeOfParameterizedEntityName:
case Value::Kind::TypeOfMemberName:
return true;
}
}

auto TypeChecker::DeclareAliasDeclaration(Nonnull<AliasDeclaration*> alias,
const ImplScope& enclosing_scope)
-> ErrorOr<Success> {
CARBON_RETURN_IF_ERROR(TypeCheckExp(&alias->target(), enclosing_scope));

if (!IsValidTypeForAliasTarget(&alias->target().static_type())) {
return CompilationError(alias->source_loc())
<< "invalid target for alias declaration";
}

CARBON_ASSIGN_OR_RETURN(Nonnull<const Value*> target,
InterpExp(&alias->target(), arena_, trace_stream_));

SetConstantValue(alias, target);
alias->set_static_type(&alias->target().static_type());
return Success();
}

auto TypeChecker::TypeCheck(AST& ast) -> ErrorOr<Success> {
ImplScope impl_scope;
for (Nonnull<Declaration*> declaration : ast.declarations) {
Expand Down Expand Up @@ -2472,6 +2538,9 @@ auto TypeChecker::TypeCheckDeclaration(Nonnull<Declaration*> d,
case DeclarationKind::SelfDeclaration: {
CARBON_FATAL() << "Unreachable TypeChecker `Self` declaration";
}
case DeclarationKind::AliasDeclaration: {
return Success();
}
}
return Success();
}
Expand Down Expand Up @@ -2534,6 +2603,12 @@ auto TypeChecker::DeclareDeclaration(Nonnull<Declaration*> d,
case DeclarationKind::SelfDeclaration: {
CARBON_FATAL() << "Unreachable TypeChecker declare `Self` declaration";
}

case DeclarationKind::AliasDeclaration: {
auto& alias = cast<AliasDeclaration>(*d);
CARBON_RETURN_IF_ERROR(DeclareAliasDeclaration(&alias, enclosing_scope));
break;
}
}
return Success();
}
Expand Down
3 changes: 3 additions & 0 deletions explorer/interpreter/type_checker.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ class TypeChecker {
auto DeclareChoiceDeclaration(Nonnull<ChoiceDeclaration*> choice,
const ImplScope& enclosing_scope)
-> ErrorOr<Success>;
auto DeclareAliasDeclaration(Nonnull<AliasDeclaration*> alias,
const ImplScope& enclosing_scope)
-> ErrorOr<Success>;

// Find all of the GenericBindings in the given pattern.
void CollectGenericBindingsInPattern(
Expand Down
2 changes: 2 additions & 0 deletions explorer/syntax/lexer.lpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
%s AFTER_OPERAND

/* table-begin */
ALIAS "alias"
AMPERSAND "&"
AND "and"
API "api"
Expand Down Expand Up @@ -137,6 +138,7 @@ string_literal \"([^\\\"\n\v\f\r]|\\.)*\"
%}

/* table-begin */
{ALIAS} { return SIMPLE_TOKEN(ALIAS); }
{AMPERSAND} { return SIMPLE_TOKEN(AMPERSAND); }
{AND} { return SIMPLE_TOKEN(AND); }
{API} { return SIMPLE_TOKEN(API); }
Expand Down
7 changes: 7 additions & 0 deletions explorer/syntax/parser.ypp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
%type <bool> api_or_impl
%type <Nonnull<Declaration*>> declaration
%type <Nonnull<FunctionDeclaration*>> function_declaration
%type <Nonnull<AliasDeclaration*>> alias_declaration
%type <std::vector<Nonnull<Declaration*>>> declaration_list
%type <Nonnull<Statement*>> statement
%type <Nonnull<If*>> if_statement
Expand Down Expand Up @@ -173,6 +174,7 @@
%token
// Most tokens have their spelling defined in lexer.lpp.
// table-begin
ALIAS
AMPERSAND
AND
API
Expand Down Expand Up @@ -864,6 +866,9 @@ variable_declaration: identifier COLON pattern
std::nullopt);
}
;
alias_declaration: ALIAS identifier EQUAL expression SEMICOLON
{ $$ = arena->New<AliasDeclaration>(context.source_loc(), $2, $4); }
;
alternative:
identifier tuple
{ $$ = arena->New<AlternativeSignature>(context.source_loc(), $1, $2); }
Expand Down Expand Up @@ -945,6 +950,8 @@ declaration:
YYERROR;
}
}
| alias_declaration
{ $$ = $1; }
;
impl_kind:
// Internal
Expand Down
40 changes: 40 additions & 0 deletions explorer/testdata/alias/class_alias.carbon
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// RUN: %{explorer} %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
// AUTOUPDATE: %{explorer} %s
// CHECK: result: 123

package ExplorerTest api;

interface Addable {
fn Add[me: Self](k: i32) -> Self;
}
impl i32 as Addable {
fn Add[me: Self](k: i32) -> Self { return me + k; }
}

class Class { var n: i32; }
class GenericClass(T:! Addable) {
var m: T;
fn Get[me: Self](n: i32) -> T { return me.m.Add(n); }
}

alias ClassAlias = Class;
alias GenericClassAlias = GenericClass;
alias ClassSpecializationAlias = GenericClassAlias(i32);

fn Main() -> i32 {
var a: Class = {.n = 1};
var b: ClassAlias = a;

var c: GenericClass(i32) = {.m = 2};
var d: GenericClassAlias(i32) = c;
var e: ClassSpecializationAlias = c;
// TODO: Switch to using Print here once it supports printing integers.
return 100 * b.n + 10 * d.Get(0) + e.Get(1);
}
16 changes: 16 additions & 0 deletions explorer/testdata/alias/fail_alias_expression.carbon
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// RUN: %{not} %{explorer} %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
// RUN: %{not} %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
// AUTOUPDATE: %{explorer} %s

package ExplorerTest api;

// CHECK: COMPILATION ERROR: {{.*}}/explorer/testdata/alias/fail_alias_expression.carbon:[[@LINE+1]]: invalid target for alias declaration
alias A = 5;

fn Main() -> i32 { return 0; }
Loading

0 comments on commit 471bb56

Please sign in to comment.